Unity接入微信支付SDK 2022年版安卓篇

最近1年轉了UE開發,博客更新的比較少,技術棧寬了不少,以後有空盡量多更新,也方便總結記憶

Unity接入微信支付整個過程坑比較多,網上之前的教程要麼比較老,要麼比較零碎,只能東拼西湊摸索,跑通後還是總結記錄下吧

開發環境,Windows11,Unity2020.3.24f1c2,Android Studio2021.2.1 Patch2

1.註冊微信開放平台,註冊應用,申請微信支付,來獲取調取SDK的必要參數,此處不展開了(//open.weixin.qq.com/)
2.確保開發環境裝好,jdk,sdk等,Unity的安卓環境可以用Unity Hub自帶的裝好,也可以用全局環境變量的
3.Android Studio端的開發,導出aar

新建工程

設置包名,注意要和unity工程的包名一致

切換到Project視圖,新建module,要選擇Android Library

保持包名一致

需要將微信的庫和Unity(微信支付成功後需要安卓端主動通知Unity端)的庫都加入到工程中的lib文件夾中

Unity的庫在這個位置,classes.jar文件,根據自己的需求來選擇il2cpp還是mono,release還是dev,一共四種

將Unity的庫照圖右鍵添加到gradle配置中

新版的classes.jar文件里不包含UnityPlayerActivity類,需要從你編輯器的這個路徑里Editor\2020.3.24f1c2\Editor\Data\PlaybackEngines\AndroidPlayer\Source\com\unity3d\player拷貝出UnityPlayerActivity.java文件

然後回到編輯器中,取消勾選圖片中的選線,可以讓包名文件夾分級

新建一級微信package

直接把UnityPlayerActivity.java文件拖到wx下面

然後我們開始引入微信的庫,微信官方已經不直接提供jar或者aar的下載,已改用 gradle 形式,發佈到 Maven Central

這樣方便安卓工程集成,但是我們要打出aar包給unity使用,直接導出aar的話並不包含微信的庫,有兩種方法來解決
1)是谷歌出的一款插件,通過Unity插件Play Services Resolver for Unity在Unity端補全aar庫的依賴關係,這個甚至可以補全ios平台的依賴,但我用的不多,引路官方倉庫://github.com/googlesamples/unity-jar-resolver
2)通過Google Maven下載,搜索wechat,然後選擇版本,選擇aar包下載到本地

aar包中是包含jar的,我們需要用解壓軟件把jar包提取出來,然後改好名字

最後將微信的jar包放到lib下面並右鍵添加到工程中

這時我們rebuild工程,就可以看到打出的aar包了,在這個路徑下

還有一點,這個aar包中包含unity的庫,我們不需要,這會導致我們打包報錯,修改build.gradle,將unity的庫改為只引用,打包不包含

至此我們的lib依賴部分完成,下面開始添加Java的邏輯,首先是兩個微信的Activity,用於安卓到Unity端的調用,一個登錄,一個支付

代碼在這裡,記住,別忘了把APP_ID換成你們自己申請的
WXEntryActivity

package com.myvision.myapp.wxapi;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.Toast;

import com.tencent.mm.opensdk.constants.ConstantsAPI;
import com.tencent.mm.opensdk.modelbase.BaseReq;
import com.tencent.mm.opensdk.modelbase.BaseResp;
import com.tencent.mm.opensdk.modelbiz.SubscribeMessage;
import com.tencent.mm.opensdk.modelbiz.WXLaunchMiniProgram;
import com.tencent.mm.opensdk.modelbiz.WXOpenBusinessView;
import com.tencent.mm.opensdk.modelbiz.WXOpenBusinessWebview;
import com.tencent.mm.opensdk.modelmsg.SendAuth;
import com.tencent.mm.opensdk.modelmsg.ShowMessageFromWX;
import com.tencent.mm.opensdk.modelmsg.WXAppExtendObject;
import com.tencent.mm.opensdk.modelmsg.WXMediaMessage;
import com.tencent.mm.opensdk.openapi.IWXAPI;
import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler;
import com.tencent.mm.opensdk.openapi.WXAPIFactory;

import java.lang.ref.WeakReference;

public class WXEntryActivity extends Activity implements IWXAPIEventHandler{
    private static String TAG = "MicroMsg.WXEntryActivity";

    private IWXAPI api;
    private MyHandler handler;
    public static String wxAPPID = "換成你們自己";
    private static class MyHandler extends Handler {
        private final WeakReference<WXEntryActivity> wxEntryActivityWeakReference;

        public MyHandler(WXEntryActivity wxEntryActivity){
            wxEntryActivityWeakReference = new WeakReference<WXEntryActivity>(wxEntryActivity);
        }

        @Override
        public void handleMessage(Message msg) {
            int tag = msg.what;

        }
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        api = WXAPIFactory.createWXAPI(this, wxAPPID, false);
        handler = new MyHandler(this);

        try {
            Intent intent = getIntent();
            api.handleIntent(intent, this);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);

        setIntent(intent);
        api.handleIntent(intent, this);
    }

    @Override
    public void onReq(BaseReq req) {
        switch (req.getType()) {
            case ConstantsAPI.COMMAND_GETMESSAGE_FROM_WX:
                goToGetMsg();
                break;
            case ConstantsAPI.COMMAND_SHOWMESSAGE_FROM_WX:
                goToShowMsg((ShowMessageFromWX.Req) req);
                break;
            default:
                break;
        }
        finish();
    }

    @Override
    public void onResp(BaseResp resp) {
        int result = 0;

        switch (resp.errCode) {
            case BaseResp.ErrCode.ERR_OK:

                break;
            case BaseResp.ErrCode.ERR_USER_CANCEL:

                break;
            case BaseResp.ErrCode.ERR_AUTH_DENIED:

                break;
            case BaseResp.ErrCode.ERR_UNSUPPORT:

                break;
            default:
                break;
        }

        Toast.makeText(this, getString(result) + ", type=" + resp.getType(), Toast.LENGTH_SHORT).show();


        if (resp.getType() == ConstantsAPI.COMMAND_SUBSCRIBE_MESSAGE) {
            SubscribeMessage.Resp subscribeMsgResp = (SubscribeMessage.Resp) resp;
            String text = String.format("openid=%s\ntemplate_id=%s\nscene=%d\naction=%s\nreserved=%s",
                    subscribeMsgResp.openId, subscribeMsgResp.templateID, subscribeMsgResp.scene, subscribeMsgResp.action, subscribeMsgResp.reserved);

            Toast.makeText(this, text, Toast.LENGTH_LONG).show();
        }

        if (resp.getType() == ConstantsAPI.COMMAND_LAUNCH_WX_MINIPROGRAM) {
            WXLaunchMiniProgram.Resp launchMiniProgramResp = (WXLaunchMiniProgram.Resp) resp;
            String text = String.format("openid=%s\nextMsg=%s\nerrStr=%s",
                    launchMiniProgramResp.openId, launchMiniProgramResp.extMsg,launchMiniProgramResp.errStr);

            Toast.makeText(this, text, Toast.LENGTH_LONG).show();
        }

        if (resp.getType() == ConstantsAPI.COMMAND_OPEN_BUSINESS_VIEW) {
            WXOpenBusinessView.Resp launchMiniProgramResp = (WXOpenBusinessView.Resp) resp;
            String text = String.format("openid=%s\nextMsg=%s\nerrStr=%s\nbusinessType=%s",
                    launchMiniProgramResp.openId, launchMiniProgramResp.extMsg,launchMiniProgramResp.errStr,launchMiniProgramResp.businessType);

            Toast.makeText(this, text, Toast.LENGTH_LONG).show();
        }

        if (resp.getType() == ConstantsAPI.COMMAND_OPEN_BUSINESS_WEBVIEW) {
            WXOpenBusinessWebview.Resp response = (WXOpenBusinessWebview.Resp) resp;
            String text = String.format("businessType=%d\nresultInfo=%s\nret=%d",response.businessType,response.resultInfo,response.errCode);

            Toast.makeText(this, text, Toast.LENGTH_LONG).show();
        }

        if (resp.getType() == ConstantsAPI.COMMAND_SENDAUTH) {
            SendAuth.Resp authResp = (SendAuth.Resp)resp;
            final String code = authResp.code;

        }
        finish();
    }

    private void goToGetMsg() {
       // Intent intent = new Intent(this, GetFromWXActivity.class);
      //  intent.putExtras(getIntent());
      //  startActivity(intent);
        finish();
    }

    private void goToShowMsg(ShowMessageFromWX.Req showReq) {
        WXMediaMessage wxMsg = showReq.message;
        WXAppExtendObject obj = (WXAppExtendObject) wxMsg.mediaObject;

        StringBuffer msg = new StringBuffer();
        msg.append("description: ");
        msg.append(wxMsg.description);
        msg.append("\n");
        msg.append("extInfo: ");
        msg.append(obj.extInfo);
        msg.append("\n");
        msg.append("filePath: ");
        msg.append(obj.filePath);

 //       Intent intent = new Intent(this, ShowFromWXActivity.class);
  //      intent.putExtra(Constants.ShowMsgActivity.STitle, wxMsg.title);
  //      intent.putExtra(Constants.ShowMsgActivity.SMessage, msg.toString());
 //       intent.putExtra(Constants.ShowMsgActivity.BAThumbData, wxMsg.thumbData);
 //       startActivity(intent);
        finish();
    }
}

WXPayEntryActivity

package com.myvision.myapp.wxapi;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;

import com.tencent.mm.opensdk.constants.ConstantsAPI;
import com.tencent.mm.opensdk.modelbase.BaseReq;
import com.tencent.mm.opensdk.modelbase.BaseResp;
import com.tencent.mm.opensdk.openapi.IWXAPI;
import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler;
import com.tencent.mm.opensdk.openapi.WXAPIFactory;
import com.unity3d.player.UnityPlayer;


public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler{

    public static String GameObjectName;
    public static String CallBackFuncName;

    private static final String TAG = "WXPayEntryActivity";
    private IWXAPI api;
    public static String wxAPPID = "換成你們自己";		// 在調用微信支付處,設置該變量值

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        // setContentView(R.layout.wx_pay_result);
        //

        api = WXAPIFactory.createWXAPI(this, wxAPPID);
        api.handleIntent(getIntent(), this);
        //UnityPlayer.UnitySendMessage(GameObjectName,CallBackFuncName,"微信API_ING");
    }

    @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)
    {
        showText("onPayFinish, errCode = " + resp.errCode);

        if (resp.errCode != 0 && resp.errCode != -2)
        {
            showToast(this, "微信支付失敗, errCode:" + resp.errCode);
        }

        if (resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX)
        {
            // AlertDialog.Builder builder = new AlertDialog.Builder(this);
            // builder.setTitle("提示");
            // builder.setMessage("微信支付結果:" + resp.errCode);
            // builder.show();

            if(resp.errCode == 0)
            {
                // PaySuccess(this);	// 可在此處,添加應用自己的支付結果統計相關邏輯
                showToast(this, "微信支付成功");
            }
            else if(resp.errCode == -2) showToast(this, "用戶取消支付");
            else showToast(this, "支付失敗,其他異常情形" );
        }
        showToast(this, GameObjectName+","+CallBackFuncName );
        UnityPlayer.UnitySendMessage(GameObjectName,CallBackFuncName,"支付回調:"+resp.errCode);
        this.finish();
    }


    /** 輸出log信息 */
    public static void showText(final String info)
    {
        Log.d(TAG, info);
    }

    /** 輸出Toast消息 */
    private void showToast(Context context, final String info)
    {
        Toast.makeText(context, info, Toast.LENGTH_SHORT).show();
        Log.d(TAG, info);
    }
}

然後我們需要新建一個繼承自UnityPlayerActivity的MainActivity,裏面目前只實現的創建wxapi和一個支付邏輯

package com.myvision.myapp.wx;

import android.os.Bundle;

import com.myvision.myapp.wxapi.WXPayEntryActivity;
import com.tencent.mm.opensdk.modelpay.PayReq;
import com.tencent.mm.opensdk.openapi.IWXAPI;
import com.tencent.mm.opensdk.openapi.WXAPIFactory;

public class MainActivity extends UnityPlayerActivity {
    public static String APP_ID;

    private PayReq req = new PayReq();
    private IWXAPI wxAPI = null;

    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        APP_ID = "換成你們自己";
        WechatInit(APP_ID);
    }

    public void WechatInit(String appid){
        APP_ID = appid;

        if(wxAPI==null){
            wxAPI = WXAPIFactory.createWXAPI(this,appid);
            wxAPI.registerApp(appid);
        }
    }

    public boolean IsWechatInstalled(){
        return wxAPI.isWXAppInstalled();
    }

    public void WeChatPayReq(String APP_ID,String MCH_ID,String prepayid,String packageValue,String noncestr,String timestamp,String sign,String callBackObjectName,String CallBackFuncName) {
        WXPayEntryActivity.GameObjectName = callBackObjectName;
        WXPayEntryActivity.CallBackFuncName = CallBackFuncName;
        req.appId = APP_ID;
        req.partnerId = MCH_ID;
        req.prepayId = prepayid;
        req.packageValue =packageValue;
        req.nonceStr = noncestr;
        req.timeStamp = timestamp;
        req.sign = sign;
        wxAPI.sendReq(req);
    }
}

堅持住夥計!Rebuild Project!我們的aar就新鮮出爐了!我為你感到自豪!

4.Unity端的開發
首先新建好unity工程,確保包名一致,把我們的aar丟到圖中位置

切換到安卓平台,設置下你的keystore簽名,沒有這個的話不能正常喚起微信(關聯Tip1),勾選Custon Main Manifest,我們需要創建一個自定義的AndroidManifest.xml文件

AndroidManifest這樣寫

<?xml version="1.0" encoding="utf-8"?>
<!-- GENERATED BY UNITY. REMOVE THIS COMMENT TO PREVENT OVERWRITING WHEN EXPORTING AGAIN-->
<manifest
    xmlns:android="//schemas.android.com/apk/res/android"
    package="com.myvision.myapp"
    xmlns:tools="//schemas.android.com/tools">

  <queries>
    <package android:name="com.tencent.mm" />   // 指定微信包名
  </queries>

  <application>
    <activity android:name="com.myvision.myapp.wx.MainActivity"     
              android:exported="true"
              android:theme="@style/UnityThemeSelector">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
      <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
    </activity>
    <activity
        android:name="com.myvision.myapp.wxapi.WXEntryActivity"
        android:exported="true"
        android:launchMode="singleTask">
    </activity>
    <activity
    android:name="com.myvision.myapp.wxapi.WXPayEntryActivity"
    android:exported="true"
    android:launchMode="singleTask">
    </activity>
  </application>
</manifest>
 
 

注意安卓11以上,一定要加這段,否則無法調起微信(落淚)

 <queries>
    <package android:name="com.tencent.mm" />   // 指定微信包名
  </queries>

最後一的C#做一個簡單的調用

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Test : MonoBehaviour
{
    public Text ui;
    public Text ui2;
    private AndroidJavaObject jo= null;
    // Start is called before the first frame update

    public void Init() {
        AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        jo = jc.GetStatic<AndroidJavaObject>("currentActivity");
    }

    public void CheckWX() {
        bool bo = true;
        bo = jo.Call<bool>("IsWechatInstalled");
        ui.text = bo.ToString();
    }

    public void WXPay() {
        jo.Call("WeChatPayReq", 
            "你的APP_ID",
            "",
            "",
            "com.myvision.myapp",
            "",
            "",
            "",
            "Test",
            "WXCallback");
    }

    public void WXCallback(string str) {
        print("WXCallback");
        ui2.text = str;
    }
}

Editor裏面大概就是這個樣子,自己給按鈕綁定好對應事件

然後就可以打包了,簽過名打出包來以後裝在手機上,要再下載微信的簽名獲取工具://res.wx.qq.com/wxdoc/dist/assets/media/Gen_Signature_Android.e481f889.zip
在裏面輸入包名獲取簽名,把簽名填在微信開平平台對應的app信息裏面,否則無法正常調起

平台上更新好籤名後(審核要半天左右),再重新運行app,就可以調用微信支付了,我目前還沒接後端服務器部分,所以還沒有真實的微信支付參數,但傳入app_id後就可以正常調微信(但是沒調起來,需要支付相關參數正確),以及接收到安卓傳來的支付回調給Unity端,所以還不算完整流程,後面有機會再詳細補充下

完畢,喜歡的同學請三連支持,謝謝~

感謝以下博主的分享:

//www.bilibili.com/video/BV1Dr4y1T7nE/?spm_id_from=333.788.recommend_more_video.-1&vd_source=4f7f14539886b3de3b8a98d2e2b3c90d

//www.bilibili.com/video/BV1Rq4y1S7qP?p=10&vd_source=4f7f14539886b3de3b8a98d2e2b3c90d

//blog.csdn.net/linxinfa/article/details/117083637

//blog.csdn.net/weixin_45724919/article/details/126139229

//blog.csdn.net/Sun_XiaoChuan/article/details/110118309

//blog.csdn.net/weixin_42414707/article/details/93844385

//blog.csdn.net/songyu_95/article/details/88248350

Tags: