支付相关—支付宝小程序非同一主体下多商户进行收款

1,235 阅读6分钟

官方文档的描述

小程序非同一主体下的商家进行收款

普通小程序由于没有授权回调地址,无法完成第三方授权。需要完成以上需求,目前有三种方案:

  1. 商户账号在 商家平台 > 产品中心 > 产品详情页面 点击 立即开通,填写并提交相关信息。详情可查看 开通产品。开通完成后请在产品详情页点击 管理小程序 APPID,关联绑定服务商拉支付的小程序应用 A。

  2. 服务商只有一个小程序平台,商家收款都在这个小程序中支付的场景。服务商开发一个小程序应用 A 并上线,然后再创建一个第三方应用 B(可以是非小程序第三方应用),创建第三应用 B 后添加 JSAPI 支付产品和获取用户信息的功能,然后生成第三方授权链接让商家登录账号后授权给第三方应用 B。授权完成后通过第三方应用 B 的 APPID 和商家授权的 app_auth_token 在服务端调用 alipay.trade.create(统一收单交易创建接口),并在入参 op_app_id 中传入小程序经营主体 APPID(指在服务商小程序中,拉起收银台支付时,对应的小程序应用的 APPID,此场景应填入小程序应用 A),创建交易获取 tradeNO 参数,将 tradeNO 参数通过 my.request 接口传到小程序应用 A 中,用 my.tradePay 接口唤起支付,即可实现收款到商家账号中。 注意:上述开发过程中可能会遇到获取 user_id 授权问题,为方便接口调用,服务商需要再将自己的小程序应用 A 授权给自己的第三方应用 B,授权成功后也会获得代表小程序 A 的 app_auth_token。小程序应用 A 中用户授权产生的 auth_code 需要通过第三方应用 B 在 alipay.system.oauth.token(换取授权访问令牌) 接口换 auth_token,此接口调用的时候需要加上代表小程序 A 的 app_auth_token 调用才可以成功获取到 user_id,详情请查看 获取会员基础信息三方应用授权

  3. 每个商家都有自己的小程序进行收款,可上服务市场给商家订购的场景。通过创建小程序第三方应用,在第三方应用中创建小程序模板,然后再帮助商家创建小程序和签约 JSAPI 支付进行授权。完成授权后,给商家小程序上传小程序版本,商家可在自己的小程序中进行收款。详细请参考 三方业务

一、简单概括一下:需要完成的配置

准备两个账号,开放平台和商家平台不是同一个账号

  1. 开放平台创建小程序A,第三方应用B

  1. JSAPI支付产品开通并且绑定小程序A的APPID

  1. 商家授权token给第三方应用B

前期工作已经完成,小程序和第三方应用的密钥需要保存

二、换取授权访问令牌接口(获取UserID)

需要完成两个接口的调用, alipay.trade.create(统一收单交易创建接口), alipay.system.oauth.token(换取授权访问令牌)

本次代码中获取的是userID(2088开头的商户号),openID自行配置

  1. 首先在小程序IDE(支付宝官方的开发工具)去获取code,发送到服务端接口,使用code换取userID
  //获取userid
    my.getAuthCode({
      scopes: 'auth_base',
      success: res => {
        const authCode = res.authCode;
        console.log(authCode)
        // 在服务端获取用户信息
        my.request({
          url: this.data.base_url + '/aliPayUserId',
          method: "GET",
          data: {
            code: authCode
          },
          success(res) {
            // 获取需要的用户信息
            if (!res.data.data) return
            //存储在缓存中
            my.setStorageSync({
              key: 'userId',
              data: {
                userId: res.data.data
              },
            })
          }
        })
      },
      fail: err => {
        console.log('my.getAuthCode 调用失败', err)
      }
    });
  
  1. 服务端调用编写alipay.system.oauth.token接口

此处的APPID、PublicKey、PrivateKey均使用小程序A


    @GetMapping("/aliPayUserId")
    public ApiRes getAliPayUserId(String code) throws AlipayApiException, JsonProcessingException {
        
        //初始化AlipayClient
     AlipayClient alipayClient = aliPayClientService.getAlipayClient(aliPayPublicKey, aliPayPrivateKey, aliPayAppId);
        // 构造请求参数以调用接口
    AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
        //填写请求参数
    request.setCode(code);
        // 设置授权方式
    request.setGrantType("authorization_code");
        //获取响应
    AlipaySystemOauthTokenResponse response = alipayClient.execute(request);
        System.out.println(response.getBody());
        String body = response.getBody();
        ObjectMapper objectMapper = new ObjectMapper();
        JsonNode rootNode = objectMapper.readTree(body);
        String user_id = rootNode.get("alipay_system_oauth_token_response").get("user_id").asText();
        return ApiRes.ok(user_id);
    }

如果使用第三方应用A配置存在弊端:

1、直接使用第三方应用B 报错示例 isv.unmatched-app-id(调用接口的应用标识(app_id)与令牌授权的应用不相符)

2、需要将小程序应用A授权给第三方应用B,获取app_auth_token

代码中需要添加 request.putOtherTextParam("app_auth_token",app_auth_token)

此时会发现开放平台中版本管理已禁用

解决方案:

1、直接使用小程序的应用配置即可

2、详情见提交审核小程序 - 支付宝文档中心

三、统一收单交易创建接口实现(获取tradeNO)

  1. 小程序IDE端编写

<!---->

      onPayClick() {
        if(this.data.amount.length>=5){
             return my.alert({
              title:'金额超出限制!'
             })
        }
        let res = my.getStorageSync({ key: 'userId' });
        console.log(res.data)
        //调用下单接口
        my.request({
          url: this.data.base_url + '/payOrders',
          method: 'POST',
          data: {
            appId: this.data.appid,
            amount: this.data.amount,
            mchOrderNo: this.data.mchOrderNo,
            wayCode: 'ALI_JSAPI',
            authCode: 'authCode',
            buyerUserId: my.getStorageSync({ key: 'userId' }).data.userId
          }
        }).then(res => {
          let payData = JSON.parse(res.data.data.payData)
          //调起收银台
          if (res.data) {
            my.tradePay({
              tradeNO: payData.alipayTradeNo,
              success: res => {
                my.alert({
                  title: '支付中...',
                  content: this.data.amount + '元'
                })
                //轮询订单状态 请求服务端 此处暂未编写
              },
              fail: error => {
                console.error('调用 my.tradePay 失败: ', JSON.stringify(error));
              }
            })
          }
        })
      },
  1. 服务端代码

此接口不方便贴出,请参照官方示例自行编写

小程序文档 - 支付宝文档中心

此处的AlipayClient构造参数APPID、privateKey均使用第三方应用B

AlipayTradeCreateRequest对象 添加此段代码 request.putOtherTextParam("app_auth_token",商家授权的token)

根据token来确定是哪个商家来进行收款


    package com.java.sdk.demo;

    import com.alipay.api.AlipayApiException;
    import com.alipay.api.AlipayClient;
    import com.alipay.api.DefaultAlipayClient;
    import com.alipay.api.AlipayConfig;
    import com.alipay.api.response.AlipayTradeCreateResponse;
    import com.alipay.api.domain.AlipayTradeCreateModel;
    import com.alipay.api.request.AlipayTradeCreateRequest;
    import com.alipay.api.domain.ExtendParams;

    import com.alipay.api.FileItem;
    import java.util.Base64;
    import java.util.ArrayList;
    import java.util.List;

    public class AlipayTradeCreate {

        public static void main(String[] args) throws AlipayApiException {
            // 初始化SDK
            AlipayClient alipayClient = new DefaultAlipayClient(getAlipayConfig());

            // 构造请求参数以调用接口
            AlipayTradeCreateRequest request = new AlipayTradeCreateRequest();
            AlipayTradeCreateModel model = new AlipayTradeCreateModel();
            
            // 设置商户订单号
            model.setOutTradeNo("20150320010101001");
            
            // 设置产品码
            model.setProductCode("JSAPI_PAY");
            
            // 设置小程序支付中
            model.setOpAppId("2014072300007148");
            
            // 设置订单总金额
            model.setTotalAmount("88.88");
            
            // 设置业务扩展参数
            ExtendParams extendParams = new ExtendParams();
            extendParams.setTradeComponentOrderId("2023060801502300000008810000005657");
            model.setExtendParams(extendParams);
            
            // 设置可打折金额
            model.setDiscountableAmount("80.00");
            
            // 设置订单标题
            model.setSubject("Iphone6 16G");
            
            // 设置订单附加信息
            model.setBody("Iphone6 16G");
            
            // uid参数未来计划废弃,存量商户可继续使用,新商户请使用openid。请根据应用-开发配置-openid配置选择支持的字段。
            // model.setBuyerId("2088102146225135");
            
            // 设置买家支付宝用户唯一标识
            model.setBuyerOpenId("074a1CcTG1LelxKe4xQC0zgNdId0nxi95b5lsNpazWYoCo5");
            
            // 设置商户门店编号
            model.setStoreId("NJ_001");
            
            request.setBizModel(model);
            // 第三方代调用模式下请设置app_auth_token
            // request.putOtherTextParam("app_auth_token", "<-- 请填写应用授权令牌 -->");

            AlipayTradeCreateResponse response = alipayClient.execute(request);
            System.out.println(response.getBody());

            if (response.isSuccess()) {
                System.out.println("调用成功");
            } else {
                System.out.println("调用失败");
                // sdk版本是"4.38.0.ALL"及以上,可以参考下面的示例获取诊断链接
                // String diagnosisUrl = DiagnosisUtils.getDiagnosisUrl(response);
                // System.out.println(diagnosisUrl);
            }
        }

        private static AlipayConfig getAlipayConfig() {
            String privateKey  = "<-- 请填写您的应用私钥,例如:MIIEvQIBADANB ... ... -->";
            String alipayPublicKey = "<-- 请填写您的支付宝公钥,例如:MIIBIjANBg... -->";
            AlipayConfig alipayConfig = new AlipayConfig();
            alipayConfig.setServerUrl("https://openapi.alipay.com/gateway.do");
            alipayConfig.setAppId("<-- 请填写您的AppId,例如:2019091767145019 -->");
            alipayConfig.setPrivateKey(privateKey);
            alipayConfig.setFormat("json");
            alipayConfig.setAlipayPublicKey(alipayPublicKey);
            alipayConfig.setCharset("UTF-8");
            alipayConfig.setSignType("RSA2");
            return alipayConfig;
        }
    }

b82d5b713761d0b795f99f9e66000f0.jpg

后续不明白的可以在开放平台寻求支付宝官方帮助,官方人员很热心 解疑答惑,在此感谢他们的帮助!