# 支持微信登录支付

“人民网+”小程序 提供了支持小程序复用微信登录支付的能力, 针对 wx.login, wx.getUserProfile, button - getphonenumber, requestPayment等常用功能,当小程序运行在 “人民网+”小程序 中时,在配置了之后,可以自动跳转的微信对应的小程序去获取相关的数据,完成业务闭环。

# 1. 快速开始

# 第一步:添加跳转页面

添加跳转页面至微信小程序中,并将微信小程序发布上线。跳转页面作为 “人民网+”小程序与微信小程序的通信桥梁,“人民网+”小程序 目前提供了模板页面快速集成,可以通过以下两种方式获取:

# 方式一:通过 开发者工具自动集成

在 开发者工具中打开小程序工程,选择小程序页面目录-右键-新增授权页,开发者工具将会自动插入相关页面 image.png

# 方式二:自行手动集成

小程序授权页面资源 (opens new window) 下载并解压到小程序中,并将页面路径添加到 app.json

# 第二步:配置关联

前往 “人民网+”小程序 管理后台【小程序管理-我的小程序-微信关联支持-关联微信小程序】进行配置

配置参数

小程序原始id:可在微信公众平台-设置-基本设置-页面底部-原始 id

关联页面:第一步中添加的对应页面路径

# 第三步:APP 实现接口

集成微信开放 SDK,实现打开小程序以及接受回调

# 方式一

集成 “人民网+”小程序 微信扩展 SDK

# 方式二

# iOS 示例代码
// 注入登录方法
    [[FATClient sharedClient] registerExtensionApi:@"login" handler:^(FATAppletInfo *appletInfo, id param, FATExtensionApiCallback callback) {
        NSString *wxid = appletInfo.wechatLoginInfo[@"wechatOriginId"];
        NSString *path = appletInfo.wechatLoginInfo[@"profileUrl"];
        BOOL canWXLogin = [WXApi isWXAppInstalled] && [wxid length] > 0 && [path length] > 0;
        if (!canWXLogin) {
            callback(FATExtensionCodeFailure, @{@"errMsg" : @"login:fail"});
            return;
        }
        self.callback = callback;
        WXLaunchMiniProgramReq *req = [WXLaunchMiniProgramReq object];
        req.userName = wxid;
        req.path = path;
        req.miniProgramType = WXMiniProgramTypeRelease;
        [WXApi sendReq:req completion:^(BOOL success) {
            NSLog(@"打开微信:%d", success);
        }];
    }];

// 注入支付方法
    [[FATClient sharedClient] registerExtensionApi:@"requestPayment" handler:^(FATAppletInfo *appletInfo, id param, FATExtensionApiCallback callback) {
        FATAppletInfo *appInfo = [[FATClient sharedClient] currentApplet];
        NSDictionary *info = appInfo.wechatLoginInfo;
        NSDictionary *dataDic = param;
        NSString *payString = [NSString stringWithFormat:@"?appId=%@&nonceStr=%@&package=%@&paySign=%@&signType=%@&timeStamp=%@&type=%@", dataDic[@"appId"], dataDic[@"nonceStr"], dataDic[@"package"], dataDic[@"paySign"], dataDic[@"signType"], dataDic[@"timeStamp"], dataDic[@"type"]];
        WXLaunchMiniProgramReq *launchMiniProgramReq = [WXLaunchMiniProgramReq object];
        launchMiniProgramReq.userName = info[@"wechatOriginId"];
        launchMiniProgramReq.path = [NSString stringWithFormat:@"%@%@", info[@"paymentUrl"], payString];
        if (appInfo.appletVersionType == FATAppletVersionTypeRelease) {
            launchMiniProgramReq.miniProgramType = WXMiniProgramTypeRelease; //正式版
        } else if (appInfo.appletVersionType == FATAppletVersionTypeTrial) {
            launchMiniProgramReq.miniProgramType = WXMiniProgramTypePreview; //开发版
        } else {
            launchMiniProgramReq.miniProgramType = WXMiniProgramTypePreview; //体验版
        }
        [WXApi sendReq:launchMiniProgramReq completion:^(BOOL success) {
            NSLog(@"打开微信:%d", success);
        }];
    }];
        
//微信结果回调
- (void)onResp:(BaseResp *)resp {
    FATExtensionCode code = FATExtensionCodeSuccess;
    if (resp.errCode != WXSuccess) {
        code = FATExtensionCodeFailure;
    }
    if ([resp isKindOfClass:[WXLaunchMiniProgramResp class]]) {//打开小程序
        NSString *extMsg = ((WXLaunchMiniProgramResp *)resp).extMsg;
        if (extMsg.length <= 0) {
            self.callback(FATExtensionCodeFailure, nil);
        }
        else {
            self.callback(code, [self dictionaryWithJsonString:extMsg]);
        
        }
    }
}

//字符串转成字典
- (NSDictionary *)dictionaryWithJsonString:(NSString *)jsonString {
    if (jsonString == nil) return nil;

    NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
    NSError *err;
    NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData  options:NSJSONReadingMutableContainers error:&err];
    if(err) {
        NSLog(@"json解析失败:%@",err);
        return nil;
    }
    return dic;
}
# Android 示例代码
class WxModule(context: Context) : AppletApi(context) {
    private var globalWechatLoginCallback: ICallback? = null

    override fun apis(): Array<String> {
        return arrayOf("login", "requestPayment")
    }

    override fun invoke(appId: String, event: String, param: JSONObject, callback: ICallback) {
        val finAppInfo = appletApiManager.getAppletInfo(appId)
        if (finAppInfo == null) {
            callback.onFail()
            return
        }
        val wechatLoginInfo = finAppInfo.wechatLoginInfo
        if (wechatLoginInfo == null || TextUtils.isEmpty(wechatLoginInfo.wechatOriginId)) {
            callback.onFail()
            return
        }
        globalWechatLoginCallback = callback
        when (event) {
            "login"-> login(wechatLoginInfo)
            "requestPayment"-> requestPayment(wechatLoginInfo, param)
        }
    }

    private fun login(wechatLoginInfo: WechatLoginInfo){
        val req = WXLaunchMiniProgram.Req()
        // 微信小程序原始id
        req.userName = wechatLoginInfo.wechatOriginId
        // 拉起小程序页面的可带参路径,不填默认拉起小程序首页
        req.path = wechatLoginInfo.profileUrl ?: ""
        // 可选打开 开发版,体验版和正式版
        req.miniprogramType = WXLaunchMiniProgram.Req.MINIPTOGRAM_TYPE_RELEASE
        val done = WXAPiHandler.wxApi?.sendReq(req)
    }

    private fun requestPayment(wechatLoginInfo: WechatLoginInfo, param: JSONObject){
        val req = WXLaunchMiniProgram.Req()
        // 微信小程序原始id
        req.userName = wechatLoginInfo.wechatOriginId
        // 拉起小程序页面的可带参路径,不填默认拉起小程序首页
        // 此处拼接上支付所需的参数
        val profileUrlSB = StringBuilder(wechatLoginInfo.paymentUrl)
        profileUrlSB.append("?type=${param.optString("type")}")
        profileUrlSB.append("&appId=${param.optString("appId")}")
        profileUrlSB.append("&nonceStr=${param.optString("nonceStr")}")
        profileUrlSB.append("&package=${param.optString("package")}")
        profileUrlSB.append("&paySign=${param.optString("paySign")}")
        profileUrlSB.append("&signType=${param.optString("signType")}")
        profileUrlSB.append("&timeStamp=${param.optString("timeStamp")}")
        req.path = profileUrlSB.toString()
        // 可选打开 开发版,体验版和正式版
        req.miniprogramType = WXLaunchMiniProgram.Req.MINIPTOGRAM_TYPE_RELEASE
        val done = WXAPiHandler.wxApi?.sendReq(req)
    }
}

/**
 * 微信回调
 *
 * 此示例中无论是login还是requestPayment,都是通过微信小程序去执行,
 * 因此只需处理 WXLaunchMiniProgram 这一种类型的响应即可。
 */
override fun onResp(resp: BaseResp) {
    if (resp is WXLaunchMiniProgram.Resp && !TextUtils.isEmpty(resp.extMsg)) {
        val json = JSONObject()
        json.put("result", resp.extMsg)
        globalWechatLoginCallback?.onSuccess(json)
    } else {
        globalWechatLoginCallback?.onFail()
    }
}

# 2. 原理说明

具体流程图如下所示。

如上图所示,本功能的实现方式如下:

  • “人民网+”小程序调用接口(wx.login, wx.getUserProfilewx.getPhoneNumberwx.requestPayment);
  • App 端注入接口(wx.login, wx.getUserProfilewx.getPhoneNumberwx.requestPayment);
  • App端引入 wx 端 openSDK,获取管理后台配置的微信登录小程序相关信息;
  • 执行 WXLaunchMiniProgram 方法打开微信登录小程序的授权页面;
  • 小程序根据 App 的参数执行相关的微信接口获取信息,并返回 App;
  • App 把接口数据回传给 “人民网+”小程序。

请注意

微信官方公告 (opens new window),自 2021 年 12 月 27 日后不再向开发者输出用户昵称与头像信息,您可能需要自行处理获取用户头像与昵称的相关逻辑。

# 3. 帮助

若您需要使用帮助,可使用 “人民网+”小程序 中的工单功能,或加入开发者社群获得更多帮助与支持。

您也可以查看我们的官方 DEMO 项目(iOS (opens new window)Android (opens new window)),获得更多代码示例。