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

官方文档的描述

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

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

  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
kotlin 复制代码
  //获取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

ini 复制代码
@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端编写
less 复制代码
  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. 服务端代码

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

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

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

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

java 复制代码
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;
    }
}

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

相关推荐
uzong4 小时前
技术故障复盘模版
后端
GetcharZp4 小时前
基于 Dify + 通义千问的多模态大模型 搭建发票识别 Agent
后端·llm·agent
桦说编程5 小时前
Java 中如何创建不可变类型
java·后端·函数式编程
IT毕设实战小研5 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
wyiyiyi5 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
阿华的代码王国6 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
Jimmy6 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
AntBlack7 小时前
不当韭菜V1.1 :增强能力 ,辅助构建自己的交易规则
后端·python·pyqt
bobz9658 小时前
pip install 已经不再安全
后端
寻月隐君8 小时前
硬核实战:从零到一,用 Rust 和 Axum 构建高性能聊天服务后端
后端·rust·github