声明:转载请指明内容出处,谢谢!
2020.06.28 22:32 更新说明
- 正式打包后,发现微信支付无法正常调用,经发现是由于Gson无法解析JSON字符串而造成微信参数错误,因此建议使用 JSONObject 处理支付参数的数据转换;
改动代码 OnlinePayPlugin.kt:
/**
* 启动在线支付
* @param call 方法Call
*/
private fun startOnlinePay(call: MethodCall, result: MethodChannel.Result) {
callback = result //赋值结果反馈对接
val payType = call.argument<String>("type")
val payOrderInfo = call.argument<String>("payInfo")
if (payOrderInfo.isNullOrEmpty()) {
callback?.success(mapOf(
"type" to payType,
"state" to -1,
"description" to "错误:支付参数不能为空"))
callback = null
return
}
when (payType) {
"ALIPAY" -> activity?.let {
AliPayPlugin.getInstance()?.startPay(it, payOrderInfo)
}
// 微信支付的实体类使用JSONObject方式获取
"WECHAT_PAY" ->
WechatPayPlugin.getInstance()?.startPay(WechatPayParams.fromJsonObject(JSONObject(payOrderInfo)))
else -> {
result.error("ONLINE_PAY_TYPE_ERROR", "错误:未知支付类型", null)
callback = null
}
}
}
微信支付参数的实体类实现如下,WechatPayParams属于文件WechatPayPlugin.kt
data class WechatPayParams(
@JvmField
var appId: String? = BuildConfig.WX_APPID,
@JvmField
var packageValue: String? = "Sign=WXPay",
@JvmField
var partnerId: String?,
@JvmField
var prepayId: String?,
@JvmField
var sign: String? = null,
@JvmField
var nonceStr: String? = null,
@JvmField
var timeStamp: String? = null,
@JvmField
var signType: String? = null
) {
override fun toString(): String = Gson().toJson(this)
companion object {
@JvmStatic
fun fromJsonObject(jsonObject: JSONObject): WechatPayParams = WechatPayParams(
jsonObject.optString("appId"),
jsonObject.optString("packageValue"),
jsonObject.optString("partnerId"),
jsonObject.optString("prepayId"),
jsonObject.optString("sign"),
jsonObject.optString("nonceStr"),
jsonObject.optString("timeStamp"),
jsonObject.optString("signType")
)
}
}
2020.06.18 18:52 更新说明:
- 修复online_pay.dart文件中构造函数报错内容溢出问;
- 调整原生代码中支付参数为空的提示反馈方式,使用MethodChannel.Result来回调结果;
- 删除EventChannel,直接改为由startPay方法返回, 因EventChannel会导致支付结果信息缓存或者延迟;
- 示例代码中修改,添加输出结果展示。
原文内容
支付宝、微信支付是开发中经常需要用到的功能,那么如何集成支付功能到应用中呢?支付结果亦是异步的,又如何传递结果呢?带着这些疑问让我们一步步走进这学习探索之路。
在这探索之前,想问下看客朋友是否查看过官方的电池状态监控示例。如果还没有的朋友建议先看一下官方的源码!
当你对官方源码都有所了解后,那下面的内容就容易理解多了。一般情况下,原生与Flutter通讯,我们经常使用MethodChannel处理方法调用,然后用MethodChannel.Result来处理支付结果的返回。说到这里,头脑里是否有一个大致流程构思了?!
首先,集成支付SDK,这个就不多说了。
其次,把支付方法封装起来。
最后,把支付方法提供给Flutter调用,事件响应给Flutter。
1.支付宝支付辅助类代码
class AliPayPlugin private constructor() {
companion object {
private var alipay: AliPayPlugin? = null
@JvmStatic
fun getInstance(): AliPayPlugin? {
if (alipay == null) {
synchronized(AliPayPlugin::class.java) {
alipay = AliPayPlugin()
}
}
return alipay
}
}
@Suppress("unchecked_cast")
@SuppressLint("HandlerLeak")
private val mHandler = object : Handler() {
override fun handleMessage(msg: Message) {
with(LocalBroadcastManager.getInstance(GenydfApplication.instance)) {
val intent = Intent().apply {
action = OnlinePayPlugin.ACTION_ONLINE_PAY_RESULT_NOTIFIER
putExtra("payType", "ALIPAY")
}
when (msg.what) {
0x0000A -> with(PayResult(msg.obj as Map<String, String>)) {
/** 对于支付结果,请商户依赖服务端的异步通知结果。同步通知结果,仅作为支付结束的通知。 **/
val resultInfo = if (!TextUtils.isEmpty(result)) result else memo// 同步返回需要验证的信息
// 判断resultStatus 为9000则代表支付成功
when {
TextUtils.equals(resultStatus, "9000") -> intent.apply {
putExtra("message", "支付宝支付成功")
putExtra("state", 1)
}
TextUtils.equals(resultStatus, "6001") -> intent.apply {
putExtra("message", "您取消了支付宝支付")
putExtra("state", 0)
}
else -> intent.apply {
putExtra("message", "支付失败,原因:$resultInfo")
putExtra("state", -1)
}
}
}
}
sendBroadcast(intent)
}
}
}
/**
* 支付宝支付业务示例
* @param activity 上下文对象
* @param orderInfo 订单信息,来自服务器
*/
fun startPay(activity: Activity, orderInfo: String): Unit = Thread {
val result = PayTask(activity).payV2(orderInfo, true)
val msg = Message()
msg.what = 0x0000A
msg.obj = result
mHandler.sendMessage(msg)
}.start()
}
/**
* 支付结果实体类
* @param rawResult 支付Map数据结果
*/
class PayResult(rawResult: Map<String, String>?) {
/**
* @return the resultStatus
*/
var resultStatus: String? = null
private set
/**
* @return the result
*/
var result: String? = null
private set
/**
* @return the memo
*/
var memo: String? = null
private set
init {
rawResult?.let {
for (key in rawResult.keys) {
when {
TextUtils.equals(key, "resultStatus") -> resultStatus = rawResult[key]
TextUtils.equals(key, "result") -> result = rawResult[key]
TextUtils.equals(key, "memo") -> memo = rawResult[key]
}
}
}
}
override fun toString(): String = "resultStatus={$resultStatus};memo={$memo};result={$result}"
}
2.微信支付辅助类代码
/**
* 微信插件
*/
class WechatPlugin private constructor() {
companion object {
private var wechatPlugin: WechatPlugin? = null
@JvmStatic
fun getInstance(): WechatPlugin? {
if (wechatPlugin == null) {
synchronized(WechatPlugin::class.java) {
wechatPlugin = WechatPlugin()
}
}
return wechatPlugin
}
}
private val wxApi: IWXAPI? by lazy {
val api = WXAPIFactory.createWXAPI(GenydfApplication.instance, BuildConfig.WX_APPID, false)
api.registerApp(BuildConfig.WX_APPID)
api
}
/** 微信APP是否已经安装 **/
val isWXAppInstalled: Boolean by lazy { wxApi?.isWXAppInstalled ?: false }
/**
* 发送请求
* @param req 要请求的对象数据
*/
fun sendRequest(req: BaseReq?) = wxApi?.sendReq(req)
/**
* 微信登录
* @param activity 上下文对象
*/
fun login(activity: Activity) {
if (wxApi?.isWXAppInstalled != true) {
Toast.makeText(activity, "您的设备未安装微信,请安装后再登录!", Toast.LENGTH_SHORT).show()
return
}
with(SendAuth.Req()) {
scope = "snsapi_userinfo"
state = "${System.currentTimeMillis()}"
wxApi?.sendReq(this)
}
}
/**
* 回调,需要在WxEntryActivity中调用
* @param intent
* @param handler
*/
fun handleIntent(intent: Intent?, handler: IWXAPIEventHandler) {
wxApi?.handleIntent(intent, handler)
}
}
/** 微信支付 **/
class WechatPayPlugin private constructor() {
companion object {
private var wechatPay: WechatPayPlugin? = null
@JvmStatic
fun getInstance(): WechatPayPlugin? {
if (wechatPay == null) {
synchronized(WechatPayPlugin::class.java) {
wechatPay = WechatPayPlugin()
}
}
return wechatPay
}
}
/**
* 发送支付请求
* @param params 支付请求参数
*/
fun startPay(params: WechatPayParams): Unit = with(PayReq()) {
appId = params.appId
partnerId = params.partnerId
prepayId = params.prepayId
packageValue = params.packageValue
nonceStr = params.nonceStr
timeStamp = params.timeStamp
sign = params.sign
WechatPlugin.getInstance()?.sendRequest(this)
}
}
/**
* 微信支付参数实体类
* @param appId APP-ID
* @param packageValue 包名
* @param partnerId 合作者ID
* @param prepayId 准备的订单ID
* @param sign 签名字符串
* @param nonceStr 随机字符串
* @param timeStamp 时间戳
*/
data class WechatPayParams(
@JvmField
var appId: String? = BuildConfig.WX_APPID,
@JvmField
var packageValue: String? = "Sign=WXPay",
@JvmField
var partnerId: String?,
@JvmField
var prepayId: String?,
@JvmField
var sign: String? = null,
@JvmField
var nonceStr: String? = null,
@JvmField
var timeStamp: String? = null
) {
override fun toString(): String = Gson().toJson(this)
companion object {
@JvmStatic
fun fromJsonObject(jsonObject: JSONObject): WechatPayParams = WechatPayParams(
jsonObject.optString("appId"),
jsonObject.optString("packageValue"),
jsonObject.optString("partnerId"),
jsonObject.optString("prepayId"),
jsonObject.optString("sign"),
jsonObject.optString("nonceStr"),
jsonObject.optString("timeStamp"),
jsonObject.optString("signType")
)
}
}
- 在线支付插件,按照V2版本插件编写的。
下面代码中利用MethodChannel为Flutter提供调用方法,使用EventChannel为支付结果响应事件,其中这里用的是LocalBroadcastManager进行支付结果通知,我们可以把通知的优先级提高一点,防止收到通知太慢的情况发生。
class OnlinePayPlugin : FlutterPlugin, ActivityAware, MethodChannel.MethodCallHandler {
companion object {
private const val M_NAME = "com.test.trade/online-pay-plugin"
private const val E_NAME = "com.test.trade/online-pay-plugin/pay-result"
/** 在线支付结果通知 **/
const val ACTION_ONLINE_PAY_RESULT_NOTIFIER = "com.test.trade.online_pay.result"
}
private var applicationContext: Context? = null
private var activity: Activity? = null
private var payResultNotifierReceiver: BroadcastReceiver? = null
private var methodChannel: MethodChannel? = null
private var callback: MethodChannel.Result? = null
override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
onAttachedToEngine(binding.applicationContext, binding.binaryMessenger)
}
private fun onAttachedToEngine(applicationContext: Context, messenger: BinaryMessenger) {
this.applicationContext = applicationContext
payResultNotifierReceiver = newBroadcastReceiverInstance()
LocalBroadcastManager.getInstance(applicationContext)
.registerReceiver(payResultNotifierReceiver!!,
IntentFilter(ACTION_ONLINE_PAY_RESULT_NOTIFIER))
methodChannel = MethodChannel(messenger, M_NAME).apply {
setMethodCallHandler(this@OnlinePayPlugin)
}
}
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
if (payResultNotifierReceiver != null) {
LocalBroadcastManager.getInstance(applicationContext!!)
.unregisterReceiver(payResultNotifierReceiver!!)
payResultNotifierReceiver = null
}
methodChannel?.setMethodCallHandler(null)
methodChannel = null
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
onAttachedToActivity(binding.activity)
}
private fun onAttachedToActivity(activity: Activity) {
this.activity = activity
}
override fun onDetachedFromActivity() {}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {}
override fun onDetachedFromActivityForConfigChanges() {}
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
when (call.method) {
"startPay" -> startOnlinePay(call, result)
else -> result.notImplemented()
}
}
// 构建一个新的消息接收实例
private fun newBroadcastReceiverInstance() = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action == ACTION_ONLINE_PAY_RESULT_NOTIFIER) {
callback?.success(mapOf(
"type" to intent.getStringExtra("payType"),
"state" to intent.getIntExtra("state", -1),
"description" to intent.getStringExtra("message")))
callback = null
}
}
}
/**
* 启动在线支付
* @param call 方法Call
*/
private fun startOnlinePay(call: MethodCall, result: MethodChannel.Result) {
callback = result //赋值结果反馈对接
val payType = call.argument<String>("type")
val payOrderInfo = call.argument<String>("payInfo")
if (payOrderInfo.isNullOrEmpty()) {
callback?.success(mapOf(
"type" to payType,
"state" to -1,
"description" to "错误:支付参数不能为空"))
callback = null
return
}
when (payType) {
"ALIPAY" -> activity?.let {
AliPayPlugin.getInstance()?.startPay(it, payOrderInfo)
}
"WECHAT_PAY" -> WechatPayPlugin.getInstance()?.startPay(WechatPayParams.fromJsonObject(JSONObject(payOrderInfo)))
else -> {
result.error("ONLINE_PAY_TYPE_ERROR", "错误:未知支付类型", null)
callback = null
}
}
}
}
微信WXPayEntryActivity .java的处理
public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler {
private WechatPlugin wechatPlugin;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
wechatPlugin = WechatPlugin.getInstance();
if (wechatPlugin != null)
wechatPlugin.handleIntent(getIntent(), this);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
if (wechatPlugin == null)
wechatPlugin = WechatPlugin.getInstance();
if (wechatPlugin != null)
wechatPlugin.handleIntent(getIntent(), this);
}
@Override
public void onReq(BaseReq req) {
}
@Override
public void onResp(BaseResp resp) {
if (resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) {
Intent intent = new Intent();
intent.setAction(OnlinePayPlugin.ACTION_ONLINE_PAY_RESULT_NOTIFIER);
intent.putExtra("payType", "WECHAT_PAY");
switch (resp.errCode) {
case 0: //支付成功
intent.putExtra("message", "微信支付成功");
intent.putExtra("state", 1);
break;
case -1: //支付错误
intent.putExtra("message", "微信支付失败, 错误:" + resp.errStr);
intent.putExtra("state", -1);
break;
case -2: //用户取消
intent.putExtra("message", "您已取消微信支付");
intent.putExtra("state", 0);
break;
}
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
finish();
}
}
4.Flutter部分实现,对接MethodChannel和EventChannel,类使用单例模式。
/// 在线支付插件, 单例类
class OnlinePayPlugin {
static const MethodChannel _methodChannel =
const MethodChannel('com.test.trade/online-pay-plugin');
static const EventChannel _eventChannel =
const EventChannel('com.test.trade/online-pay-plugin/pay-result');
factory OnlinePay() {
if (_instance == null) _instance = OnlinePay._internel();
return _instance;
}
OnlinePay._internel();
/// 开始支付
/// + `info` 支付信息
Future<OnlinePayResultInfo> startPay(OnlinePayInfo info) async {
var result = await _methodChannel.invokeMethod('startPay', {
'type': info.type == PayType.AliPay ? 'ALIPAY' : 'WECHAT_PAY',
'payInfo': info.payArguments is String
? info.payArguments
: json.encode(info.payArguments)
});
return OnlinePayResultInfo.fromJson(result);
}
}
/// 支付类型
enum PayType {
///支付宝支付
AliPay,
/// 微信支付
WechatPay
}
///支付信息
class OnlinePayInfo {
/// 支付类型
PayType type;
/// 支付参数
dynamic payArguments;
OnlinePayInfo({@required this.type, @required this.payArguments});
Map<String, dynamic> toJson() => {
'type': type.toString(),
'payArguments':
payArguments is String ? payArguments : json.encode(payArguments)
};
}
/// 微信支付参数
class WechatPayArgumentsInfo {
/// 应用ID
String appId;
/// 常量值:Sign=WXPay
String packageValue;
/// 合作者ID
String partnerId;
/// 预付订单ID
String prepayId;
/// 签名字符串
String sign;
/// 随机字符串
String nonceStr;
/// 时间戳
String timeStamp;
WechatPayArgumentsInfo();
factory WechatPayArgumentsInfo.fromJson(Map<String, dynamic> json) =>
WechatPayArgumentsInfo()
..appId = json['appId'] as String
..packageValue = json['packageValue'] as String
..partnerId = json['partnerId'] as String
..prepayId = json['prepayId'] as String
..sign = json['sign'] as String
..nonceStr = json['nonceStr'] as String
..timeStamp = json['timeStamp'] as String;
Map<String, dynamic> toJson() => {
'appId': appId,
'packageVakue': packageValue,
'partnerId': partnerId,
'prepayId': prepayId,
'sign': sign,
'nonceStr': nonceStr,
'timeStamp': timeStamp
};
}
/// 支付结果状态
enum OnlinePayResultState {
/// 支付成功
Success,
/// 支付失败
Fail,
/// 支付被取消
Cancel
}
/// 在线支付结果信息
class OnlinePayResultInfo {
/// 支付类型
PayType type;
/// 支付状态
OnlinePayResultState state;
/// 支付结果描述
String description;
OnlinePayResultInfo();
factory OnlinePayResultInfo.fromJson(Map<String, dynamic> json) =>
OnlinePayResultInfo()
..type = json['type'] == 'ALIPAY' ? PayType.AliPay : PayType.WechatPay
..state = json['state'] == 1
? OnlinePayResultState.Success
: (json['state'] == -1
? OnlinePayResultState.Fail
: OnlinePayResultState.Cancel)
..description = json['description'] as String;
Map<String, dynamic> toJson() => {
'type': type.toString(),
'state': state.toString(),
'description': description
};
}
5.调用测试
void test() async {
debugPrint('------>开始支付:');
var result = await OnlinePay()
.startPay(OnlinePayInfo(type: PayType.AliPay, payArguments: ''));
debugPrint('------>结果:${result.toJson()}');
debugPrint('------->支付结束');
}
输出结果:
I/flutter ( 7857): ------>开始支付:
I/flutter ( 7857): ------>结果:{type: PayType.AliPay, state: OnlinePayResultState.Fail, description: 支付失败,原因:系统繁忙,请稍后再试}
I/flutter ( 7857): ------->支付结束
网友评论