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

官方文档的描述

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

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

  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;
    }
}

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

相关推荐
闲猫1 小时前
go orm GORM
开发语言·后端·golang
丁卯4042 小时前
Go语言中使用viper绑定结构体和yaml文件信息时,标签的使用
服务器·后端·golang
bing_1585 小时前
简单工厂模式 (Simple Factory Pattern) 在Spring Boot 中的应用
spring boot·后端·简单工厂模式
天上掉下来个程小白6 小时前
案例-14.文件上传-简介
数据库·spring boot·后端·mybatis·状态模式
Asthenia04127 小时前
基于Jackson注解的JSON工具封装与Redis集成实战
后端
编程星空7 小时前
css主题色修改后会多出一个css吗?css怎么定义变量?
开发语言·后端·rust
程序员侠客行7 小时前
Spring事务原理 二
java·后端·spring
dmy8 小时前
docker 快速构建开发环境
后端·docker·容器
sjsjsbbsbsn8 小时前
Spring Boot定时任务原理
java·spring boot·后端
计算机毕设指导69 小时前
基于Springboot学生宿舍水电信息管理系统【附源码】
java·spring boot·后端·mysql·spring·tomcat·maven