Alipay (支付寶支付) 如何實現一個專案配置多個商戶資訊付款給對應商戶

dapideng發表於2019-11-07

如何實現一個專案配置多個商戶資訊付款給對應商戶

最近在對接支付寶支付時,遇到了一個問題

使用者在付款時,需要直接付款到指定支付寶賬戶。

這個需求也無可厚非,就像我們公司有四個分公司,分別在北京、上海等地,如果錢只能到總公司的賬戶上,那在財務結算的時候就很麻煩。

簡單說一下問題背景:

公司準備上線一個商城應用,有分公司和服務商的概念,意思就是收錢的不是一個人。

前一陣子,螞蟻金服投了一大筆錢,也算是達成了合作,所以在第三方支付中首選了支付寶,場景是手機網站支付,問題就在這出現了。目前支付寶只在當面付中支援配置多個收款賬戶,也是在社群裡逛了一陣子才知道這個資訊的。

方案二
方案二
螞蟻金服開發者社群原貼:如何實現一個專案配置多個商戶資訊付款給對應商戶

上面的帖子明確說明了目前實現的兩種方式,很明顯方案二看起來比較優雅,也比較好實現,但是很抱歉,手機網站支付不支援方案二。

ps:判斷是否支援方案二的方法就是去看它的文件,如果請求引數中有app_auth_token這個引數就是支援方案二的。

為了證實這個蛋疼的訊息,我又專門發了一個帖子確認了一遍

手機網站支付遇到了一些問題

管理員明確的回覆了我,只能用方案一,並且要申請N個應用,並全部上線。

Google了一下,沒搜到用這種方式實現的程式碼,只能自己寫了。。

支付寶最終是根據請求過來的appid來判斷哪一個商戶收款(也就是請求是誰的appid就收款到誰的賬號下)

所以我申請了四個應用。。。。

先定一個介面;

public interface AlipayClientConfig {
  /**
   * 閘道器.
   */
  String getGatewayUrl();
  /**
   * appid.
   */
  String getAppId();
  /**
   * 私鑰.
   */
  String getAppPrivateKey();
  /**
   * 格式 預設json.
   */
  String getFormat();
  /**
   * 編碼 預設utf-8.
   */
  String getCharset();
  /**
   * 支付寶公鑰.
   */
  String getAlipayPublicKey();
  /**
   * 簽名方式.
   */
  String getSignType();
}

在定義一個抽象類實現介面;

@Getter
@Setter
public class AlipayAbstractClientConfig implements AlipayClientConfig {

  /**
   * 閘道器.
   */
  private String gatewayUrl;
  /**
   * appid.
   */
  private String appId;
  /**
   * 私鑰.
   */
  private String appPrivateKey;
  /**
   * 格式 預設json.
   */
  private String format;
  /**
   * 編碼 預設utf-8.
   */
  private String charset;
  /**
   * 支付寶公鑰.
   */
  private String alipayPublicKey;
  /**
   * 簽名方式.
   */
  private String signType;
}

然後是四個配置檔案繼承抽象類;

@Configuration(AlipayConstant.BEIJING_CLIENT)
@ConfigurationProperties(prefix = "alipay.apenapi.beijing")
@Getter
@Setter
public class AlipayBeiJingClientConfig extends AlipayAbstractClientConfig {

}

@Configuration(AlipayConstant.NANJING_CLIENT)
@ConfigurationProperties(prefix = "alipay.apenapi.nanjing")
@Getter
@Setter
public class AlipayNanJingClientConfig extends AlipayAbstractClientConfig {

}

@Configuration(AlipayConstant.SHANGHAI_CLIENT)
@ConfigurationProperties(prefix = "alipay.apenapi.shanghai")
@Getter
@Setter
public class AlipayShangHaiClientConfig extends AlipayAbstractClientConfig {

}

@Configuration(AlipayConstant.TIANJIN_CLIENT)
@ConfigurationProperties(prefix = "alipay.apenapi.tianjin")
@Getter
@Setter
public class AlipayTianJinClientConfig extends AlipayAbstractClientConfig {

}

註解的常量資訊;

public interface AlipayConstant {

  String BEIJING_CLIENT = "BJ";

  String TIANJIN_CLIENT = "TJ";

  String NANJING_CLIENT = "NJ";

  String SHANGHAI_CLIENT = "SH";
}

再貼一下配置檔案資訊;

alipay:
  apenapi:
    tianjin:
      gateway_url: https://openapi.alipay.com/gateway.do
      app_id: 2018*********
      app_private_key: KubiKtPbIoMLITLRMKTu9MwIwE8lC9Grg5
                       6c/00rsypY/b8SQh8ydYsEPt098JYAEhS6HvLQNz6W5XvCZNeAcv307kj3RYql2W
                       Xntkh9Rr0s/cEc1WnHmBeUF3
      alipay_public_key: MIIBIjANBgkqhkistdzwIDAQAB
      format: json
      charset: utf-8
      sign_type: RSA2
    beijing:
      gateway_url: https://openapi.alipay.com/gateway.do
      app_id: 2018**********
      app_private_key: JTj9Lz6G4W3RMO8QCCK8XYWdkEH5H5I
      alipay_public_key: MIIBIjANBgB
      format: json
      charset: utf-8
      sign_type: RSA2
    shanghai:
      gateway_url: https://openapi.alipay.com/gateway.do
      app_id: 2018********
      app_private_key: MIIEvAIBADANBgcpUyx/WxcdhuAYmF3Di
      alipay_public_key: MIIBIjANBgkqhkiG9w0
      format: json
      charset: utf-8
      sign_type: RSA2
    nanjing:
      gateway_url: https://openapi.alipay.com/gateway.do
      app_id: 20181********
      app_private_key: MIIEvgIBA
      alipay_public_key: MIIBIjANBgk
      format: json
      charset: utf-8
      sign_type: RSA2

在service層呼叫時,只要動態的獲取不同的配置檔案,即可初始化不同的AlipayClient,向支付寶發起請求;

@Service
@Slf4j
@Transactional(readOnly = true)
public class AlipayServiceImpl implements AlipayService {

  @Autowired
  private AlipayDetailConfig alipayDetailConfig;

  @Autowired
  private Map<String, AlipayClientConfig> configMap;

  @Override
  public AlipayTradeWapPayResponse wapPay(AlipayWayPayRequest request) throws AlipayApiException {
    String method = "wapPay";
    log.debug("Enter {}", method);
    log.debug("WapPay form info : orderCode:【{}】 、orderTotalAmount:【{}】 "
            + "、orderSubject:【{}】 、orderDescription:【{}】 、quitUrl:【{}】 、companyCode:【{}】。",
        request.getOrderCode(), request.getOrderTotalAmount(), request.getOrderSubject(),
        request.getOrderDescription(), request.getQuitUrl(), request.getCompanyCode()
    );
    AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest();
    alipayRequest.setNotifyUrl(alipayDetailConfig.getNotifyUrl());
    alipayRequest.setReturnUrl(alipayDetailConfig.getReturnUrl());
    AlipayTradeWapPayModel payModel = new AlipayTradeWapPayModel();
    //商戶訂單號
    payModel.setOutTradeNo(request.getOrderCode());
    //訂單總金額,單位為元,精確到小數點後兩位,取值範圍[0.01,100000000]
    payModel.setTotalAmount(request.getOrderTotalAmount().toString());
    //訂單標題
    payModel.setSubject(request.getOrderSubject());
    //訂單描述
    payModel.setBody(request.getOrderDescription());
    //使用者付款中途退出返回商戶網站的地址
    payModel.setQuitUrl(request.getQuitUrl());
    //商品主型別  虛擬類商品不支援使用花唄渠道
    payModel.setGoodsType(AlipayConstant.VIRTUAL_GOODS_TYPE);
    //該筆訂單允許的最晚付款時間,逾期將關閉交易
    payModel.setTimeoutExpress(alipayDetailConfig.getTimeoutExpress());
    //銷售產品碼,與支付寶簽約的產品碼名稱
    payModel.setProductCode(AlipayConstant.WAP_PAY_PRODUCT_CODE);
    //設定回傳引數
    payModel.setPassbackParams(request.getCompanyCode());
    alipayRequest.setBizModel(payModel);
    //返回支付資訊
    AlipayClient alipayClient = getAlipayClient(request.getCompanyCode());
    log.debug("Exit {}", method);
    return alipayClient.pageExecute(alipayRequest);
  }

    /**
   * 根據分公司code返回對應的client.
   *
   * @param companyCode 分公司code.
   */
  private AlipayClient getAlipayClient(String companyCode) {
    AlipayClientConfig config = configMap.get(companyCode);
    return new DefaultAlipayClient(
        config.getGatewayUrl(),
        config.getAppId(),
        config.getAppPrivateKey(),
        config.getFormat(),
        config.getCharset(),
        config.getAlipayPublicKey(),
        config.getSignType()
    );
  }

至此,算是比較優雅的實現了付款給指定商戶賬號。

後記:微信支付早就解決了這個問題 ,也因此在社群裡,一大批吐槽者瘋狂吐槽支付寶的設計思路和實現方式。

本文由telami 創作,採用CC BY 3.0 CN協議 進行許可,可自由轉載、引用,但需署名作者且註明。

相關文章