# 支付回调说明

# 使用研学开放平台支付流程

支付流程

步骤一:开发者通过调用开放平台的支付SDK,跳转到商品展示页面

开发者调用支付SDK的代码示例:

await openxSDK.pay()

步骤二:开放平台支付系统支付完成后,回调第三方的支付回调地址

第三方回调接口注意事项:

开发者需要在研学开放管理后台 (opens new window)左侧导航栏工具管理设置工具信息时填写支付回调地址。

支付回调地址输入

以下是支付回调接口的注意事项:

  • 接口请求方式:POST
  • 接口请求类型:application/json
  • 接口入参:@RequestBody CallbackVO callbackVO,其中CallbackVO是一个被序列化的实体类对象。该对象中包含一个名为pay_resource的字段,其数据类型为String。此pay_resource字段的内容是经过AES对称加密算法处理的字符串,其加密所使用的密钥为开发者在开放平台管理后台成功创建应用后所获取的专属密钥SecretKey。开发者需确保该密钥的安全存储与保密。
  • 解密后的参数有:
参数名 释义
order_no 订单编号
product_id 商品ID
product_name 商品名称
pay_price 实付价格
pay_time 支付时间
pay_type 支付方式,当前为wechatalipay

# 第三方支付回调代码示例

支付回调方法

/**
 * 第三方付支付回调方法示例
 *
 * @param callbackVO
 * @return APIResult
 * @throws Exception
 */
@PostMapping("/callback")
public APIResult callback(@RequestBody CallbackVO callbackVO) {
    try {
        Map<String, String> map = decryptMap(callbackVO.getPay_resource(), 	         			"F1bx2C1r4GjDXbsSXjiKpdiz88TAbRTh");
        //TODO 第三方服务支付回调处理逻辑
        
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
    return new APIResult<>(200, "success");
}

/**
 * 解密密文(代码示例)
 *
 * @param encryptedData 待解密的支付密文
 * @param secretKey     密钥(开放平台管理后台创建应用后生成的密钥SecretKey)
 * @return java.util.Map<java.lang.String,java.lang.String>
 * @throws Exception
 */
private Map<String, String> decryptMap(String encryptedData, String secretKey) throws Exception {
    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
    SecretKeySpec secretKeySpec = new 				 						   				SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "AES");
    cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
    byte[] decodedBytes = Base64.getDecoder().decode(encryptedData);
    byte[] decryptedBytes = cipher.doFinal(decodedBytes);
    String decryptedString = new String(decryptedBytes, StandardCharsets.UTF_8);

    Map<String, String> decryptedMap = new TreeMap<>();
    String[] pairs = decryptedString.split("&");
    for (String pair : pairs) {
        int idx = pair.indexOf("=");
        if (idx > 0) {
            decryptedMap.put(pair.substring(0, idx), pair.substring(idx + 1));
        }
    }
    return decryptedMap;
}

CallbackVO

@Data
@ToString
public class CallbackVO implements Serializable {
    private static final long serialVersionUID = 1L;
	/** 支付回调加密密文 */
    private String pay_resource;
}

APIResult

public class APIResult<T> implements Serializable {
    private static final long serialVersionUID = 1L;
   /** 接口返回数据 */
    private T content;
    
    /** 接口返回状态码 */
    private Integer code;

    public APIResult(Integer code, T content) {
        this.content = content;
        this.code = code;
    }
}