今天花了一整天在unity上集成安卓的微信支付,把微信的相关文档反复看了很多遍,网上相关文章和资料也看了挺多。。但目标,还是无限趟坑…… 最后终于调好,在此分享一下各个坑点,以及支付集成的要点吧。
微信没有提供unity集成方法,所以我们需要使用安卓原生方法调用。
可以在C#中使用AndroidJavaClass、AndroidJavaObject来实现直接调用com.tencent.mm.sdk.openapi.WXAPIFactory的createWXAPI方法。
具体方法就不赘述了,网上很多,请自行搜索。
核心参考代码
static AndroidJavaObject WXApi {
get{
if (_wxApi == null) {
AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaClass apiFactory = new AndroidJavaClass ("com.tencent.mm.sdk.openapi.WXAPIFactory");
_wxApi = apiFactory.CallStatic<AndroidJavaObject> ("createWXAPI",
activity,
CommonSettings.WXAppID,
false
);
}
return _wxApi;
}
}
static AndroidJavaObject _wxApi;
Dictionary<string,string> paras = new Dictionary<string, string>();
//BY CG:由于wechat android sdk中的变量与签名标志不一致,所以分开写,#前面是签名属性,后面对应的SDK中变量名
paras.Add ("appid#appId", CommonSettings.WXAppID);
paras.Add ("partnerid#partnerId", CommonSettings.WXShopId); //商户号
paras.Add ("prepayid#prepayId", prepayid);
paras.Add ("package#packageValue", "Sign=WXPay"); //暂填写固定值Sign=WXPay
paras.Add ("noncestr#nonceStr", transactionId + "#" + Tools.GetRandomInt(0,100).ToString ()); //随机字符串
Int32 timestamp = (Int32)(HSTimeHelper.Now.Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
paras.Add ("timestamp#timeStamp", timestamp.ToString ());
paras.Add ("sign#sign", GenerateSignString(paras)); //应用签名
var request = new AndroidJavaObject ("com.tencent.mm.sdk.modelpay.PayReq");
foreach(var kv in paras){
request.Set (kv.Key.Split('#')[1], kv.Value);
}
bool ret = WXApi.Call<bool>("sendReq", request);
签名算法
//生成微信支付签名
private static string GenerateSignString(Dictionary<string,string> paras)
{
SortedDictionary<string, string> dict = new SortedDictionary<string, string>();
foreach(var kv in paras)
{
dict.Add(kv.Key.Split('#')[0], kv.Value);
}
List<string> tmp = new List<string>();
foreach (var kv in dict)
{
tmp.Add(string.Format("{0}={1}", kv.Key, kv.Value));
}
tmp.Add("key=" + CommonSettings.WXPaySecret);
var signTmp = string.Join("&", tmp.ToArray());
var result = MD5Utils.Encrypt(signTmp).ToUpper();
return result;
}
微信的客户端签名算法变量名与赋值变量名不一致
微信APP端SDK类com.tencent.mm.sdk.modelpay.PayReq提供的变量参数名,与最后参与签名算法的参数名是不一样的,这里要特别注意。
比如变量packageValue参与签名算法是package。
以下是必选参数的映射关系(注意大小写,左边是签名算法的参数名,右边是API中的类成员名)
appid = appId
partnerid = partnerId
prepayid = prepayId
package = packageValue
noncestr = nonceStr
timestamp = timeStamp
上面的示例代码中已经有了,请大家参考。
使用JDK1.8编译的JAR包导致unity生成APK出错
在unity打包APK的时候总是报错生成java dex的时候出错。我排查了半天,发现应该是使用的几个JAR包编译环境不一致,我在写支付回调包的时候,使用的jdk1.8版本编译的,而环境中的支付宝SDK的JAR包用的是jdk1.7
将编译环境改为jdk1.7即解决本问题。
sendReq方法并非阻塞Unity线程的,需要使用回调机制
不同于支付宝,微信支付必须写回调。并且严格要求了回调的包名和类名。
API中提供的客户端支付接口(sendReq方法)没有阻塞unity 的UI线程,在启动微信支付的activity页的同时就返回了。
所以需要我们实现WXPayEntryActivity类,我们使用UnityPlayer.UnitySendMessage来实现JAVA回调unity,以下是参考代码。
package com.hanjiasongshu.jianghux.wxapi;
import com.tencent.mm.sdk.constants.ConstantsAPI;
import com.tencent.mm.sdk.modelbase.BaseReq;
import com.tencent.mm.sdk.modelbase.BaseResp;
import com.tencent.mm.sdk.openapi.IWXAPI;
import com.tencent.mm.sdk.openapi.IWXAPIEventHandler;
import com.tencent.mm.sdk.openapi.WXAPIFactory;
import com.unity3d.player.UnityPlayer;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Intent;
import android.os.Bundle;
public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler {
private IWXAPI api;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//这里填你的微信APP ID
api = WXAPIFactory.createWXAPI(this, "");
api.handleIntent(getIntent(), this);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
api.handleIntent(intent, this);
}
@Override
public void onReq(BaseReq req) {
}
@Override
public void onResp(BaseResp resp) {
if (resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) {
int retCode = resp.errCode;
UnityPlayer.UnitySendMessage("Payment","WXPayCallback", String.valueOf(retCode));
finish();
}
}
}
如何编写“健壮”不丢充值的支付程序?
支付对接最怕的就是丢单(用户付了费但是我们没发商品),什么情况下会导致丢单呢?我们要从一般的支付流程说起。
注意几个常见的丢单点:
- 不能使用客户端微信到APP的回调来确认是否已支付成功。(原因:微信端支付成功却可能闪退、或由于各种原因回调失败)
- 服务端期望微信的http回调是可能失败的(原因:我方服务器断网、微信端出问题)
- 不能将支付凭证只保存在客户端(原因:用户换设备不能丢单)
那么我们保证不丢单的方式如下:
- 用户点击商品,发起支付
- 该用户生成transactionId(交易凭证)并保存到服务端
- 服务端根据transactionId调用统一下单接口生成prepayId
- 客户端获得prepayId,调用微信支付接口付费
- 若客户端支付失败或取消,则通知服务端删除transacionId
- 若支付成功,则服务端开启轮询(调用查询实际支付结果),询问微信支付状态
- 查到已付费或付费取消等有定论的结果后,发放商品,并通知客户端进行展现