订阅P2P大额收款账户变更

# 订阅P2P大额收款账户变更

本接口由对接P2P业务的商户提供,若开启订阅大额银行卡收款变更的开关,当大额银行卡收款账户有变更时平台会请求该接口. 接收到HTTP请求时,请响应httpcode=200(HTTP 响应状态码) ,否则会在5小时内重复发送16次通知.

# 通知参数

参数 类型 必填 描述 示例
id string Y 银行卡唯一ID 10
bankName string Y 银行名称 State Bank of India
accountNumber string Y 银行卡号 41459877630
name string Y 收款人姓名 Sachin Pujeri
ifscCode string Y IFSC SBIN0000899
branchName string Y 支行名称 sagar Bank
status int Y 银行卡当前状态
0-在线
1-下线
2-删除
0
operationType int Y 操作类型
0-新增
1-编辑
2-上线
3-下线
4-删除
0
operationTime long Y 触发时间 (时间戳:毫秒) 1705128180000
sign string Y 签名 EcCs7GlN1UzEAflgpyJ4lKIHe9+lS/gdkcEvWI
+nSlcvvz1c+Dg4Zi8sFmpYaoOMGxS3/BY66F
Gq2TXzVGhAciq2kFmwZFtYbFvr7xPTaNf/n0R
u7ZtV/jo3uBnfc9/JI6XlEWlIbPtGsVpH7CLhzgh
dqIT+YbPEdhX2ZKETFTTfIj6PzctY9eOsZ51Yb
HYnXv0jTWn1hdPoDofAF9RmoNaXK4FGYibQ
BSvVA6w4rM6hCXlbTeHdMc4askrZlrHeG3uhyD
34VlXs74cdzAZ+oROMXBcgXR4ObsJVDFz9tsJ
H92iaxlKTF0lEFZa/To5ezLpHvTOAK0uY/K6IxvO
AAg==

# 异步通知报文示例

{
  "id":"25",
  "bankName":"State Bank of India",
  "accountNumber":"41459877630",
  "name":"Sachin Pujeri",
  "ifscCode":"SBIN0000899",
  "branchName":"sagar Bank",
  "status":0,
  "operationType":0,
  "operationTime":1705128180000,
  "sign":"EcCs7GlN1UzEAflgpyJ4lKIHe9+lS/gdkcEvWI+nSlcvvz1c+Dg4Zi8sFmpYaoOMGxS3/BY66FGq2TXzVGhAciq2kFmwZFtYbFvr7xPTaNf/n0Ru7ZtV/jo3uBnfc9/JI6XlEWlIbPtGsVpH7CLhzghdqIT+YbPEdhX2ZKETFTTfIj6PzctY9eOsZ51YbHYnXv0jTWn1hdPoDofAF9RmoNaXK4FGYibQBSvVA6w4rM6hCXlbTeHdMc4askrZlrHeG3uhyD34VlXs74cdzAZ+oROMXBcgXR4ObsJVDFz9tsJH92iaxlKTF0lEFZa/To5ezLpHvTOAK0uY/K6IxvOAAg=="
}

# 异步通知代码实现Demo

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

@RestController
@RequestMapping("callback")
public class TestCallbackController {

    /**
     * 平台公钥
     */
    public static String PLATFORM_PUB_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1dad35S74jfLPbHJh8P0jDHiTvkxwrtITK97ovVu19B24UdiHyHoEZgtNlS6alFQj1ULQ71d6EPh2rWCNkS2b5HGQXwDYBtwvesVQ8h4Sf3eVPTTLGw3BS7Os4vtDEN6BezMdv3sUG2N5i6JF+5H4CQTq3MD2Cx6u/Cv7oFOdFqeDT0AH+TR7uyZxn69OtkJaHHr834EUcdShJKKMQtbC11WCcut7ilDUgdvZnThiVTq7cfl8mcC9FDKcQ9bMWamScWIB5cJQdUW23Kr0c1NvZlpgPS8U5VODM4Uc4muHJPD2cJmquuJ+4AGP36rEk27lUB3h7B6JI1QGiuh1yyPDwIDAQAB";

    /**
     * P2P大额收款账户变更回调
     * @param paramMap
     * @param response
     */
    @PostMapping("payIn")
    public void payInCallback(@RequestBody Map<String, Object> paramMap, HttpServletResponse response) throws Exception{
        boolean verifyResult = CheeseTradeRSAUtil.verifySign(paramMap, PLATFORM_PUB_KEY);
        if (verifyResult) {
            //签名验证成功
            //处理逻辑
            String name = (String)paramMap.get("name");
            String branchName = (String)paramMap.get("branchName");
            String bankName = (String)paramMap.get("bankName");
            Integer operationType = (Integer)paramMap.get("operationType");
            String id = (String)paramMap.get("id");
            String accountNumber = (String)paramMap.get("accountNumber");
            String ifscCode = (String)paramMap.get("ifscCode");
            Integer status = (Integer)paramMap.get("status");
            Long gmtEnd = (Long)paramMap.get("operationTime");
        } else {
            //签名认证失败
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
        }
    }

}
<?php
//此代码在PHP5.6版本已得到验证
class TestCallbackController 
{

    /**
     * 平台公钥
     */
    const PLATFORM_PUB_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1dad35S74jfLPbHJh8P0jDHiTvkxwrtITK97ovVu19B24UdiHyHoEZgtNlS6alFQj1ULQ71d6EPh2rWCNkS2b5HGQXwDYBtwvesVQ8h4Sf3eVPTTLGw3BS7Os4vtDEN6BezMdv3sUG2N5i6JF+5H4CQTq3MD2Cx6u/Cv7oFOdFqeDT0AH+TR7uyZxn69OtkJaHHr834EUcdShJKKMQtbC11WCcut7ilDUgdvZnThiVTq7cfl8mcC9FDKcQ9bMWamScWIB5cJQdUW23Kr0c1NvZlpgPS8U5VODM4Uc4muHJPD2cJmquuJ+4AGP36rEk27lUB3h7B6JI1QGiuh1yyPDwIDAQAB";

    /**
     * P2P大额收款账户变更回调
     * @param paramMap
     * @param response
     */
    public function payInCallback($paramMap, $response) {
        $sign = $paramMap['sign'];
        unset($result['sign']);
        $string = ConnectStrings($paramMap);
        $verifyResult = verifySign($string, $sign,self::PLATFORM_PUB_KEY);
        if ($verifyResult) {
             //签名验证成功
            //处理逻辑
            $name = $paramMap["name"];
            $branchName = $paramMap["branchName"];
            $bankName = $paramMap["bankName"];
            $operationType = $paramMap["operationType"];
            $id = $paramMap["id"];
            $accountNumber = $paramMap["accountNumber"];
            $ifscCode = $paramMap["ifscCode"];
            $status = $paramMap["status"];
            $operationTime = $paramMap["operationTime"];
        } else {
             //签名认证失败
            http_response_code(400);
        }
    }

}

//拼接字符串
function ConnectStrings($params)
{
    // 获取 map 的所有 key,并排序
    $keys = array_keys($params);
    sort($keys);

    // 遍历 key 列表,获取对应的 value,并拼接成字符串
    $str = '';

    foreach ($keys as $key) {
        $str .=  $key . '=' . $params[$key] . '&';
    }
    $str = rtrim($str, '&');
    return $str;
}   

//校验签名
function verifySign($data, $sign, $pubKey){
    $sign = base64_decode($sign);
    $key = openssl_pkey_get_public($pubKey);
    $result = openssl_verify($data, $sign, $key, OPENSSL_ALGO_SHA256) === 1;
    return $result;
}
?>

const express = require('express');
const bodyParser = require('body-parser');
//引入的签名工具类(CheeseTradeRSAUtil)的函数,签名工具类可以参照"签名&验签算法"里的DEMO
const  {verifySign}= require('./CheeseTradeRSAUtil');

const app = express();
app.use(bodyParser.json()); // 解析 JSON 请求体

// 平台公钥
const platPublicKey = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1dad35S74jfLPbHJh8P0jDHiTvkxwrtITK97ovVu19B24UdiHyHoEZgtNlS6alFQj1ULQ71d6EPh2rWCNkS2b5HGQXwDYBtwvesVQ8h4Sf3eVPTTLGw3BS7Os4vtDEN6BezMdv3sUG2N5i6JF+5H4CQTq3MD2Cx6u/Cv7oFOdFqeDT0AH+TR7uyZxn69OtkJaHHr834EUcdShJKKMQtbC11WCcut7ilDUgdvZnThiVTq7cfl8mcC9FDKcQ9bMWamScWIB5cJQdUW23Kr0c1NvZlpgPS8U5VODM4Uc4muHJPD2cJmquuJ+4AGP36rEk27lUB3h7B6JI1QGiuh1yyPDwIDAQAB
-----END PUBLIC KEY-----`;

// 回调接口
app.post('/callback/P2P', (req, res) => {
    const paramMap = req.body; // 获取请求体中的参数

    try {
        const verifyResult = verifySign(paramMap,platPublicKey);
        if (verifyResult) {
            // 签名验证成功
            const name  = paramMap.name ;
            const branchName = paramMap.branchName;
            const bankName = paramMap.bankName;
            const operationType = paramMap.operationType;
            const id = paramMap.id;
            const accountNumber = paramMap.accountNumber;
            const ifscCode = paramMap.ifscCode;
            const status = paramMap.status;
            const operationTime = paramMap.operationTime;

            // 订单通知业务处理逻辑
            console.log('签名验证成功,处理订单:', {
                name,
                branchName,
                bankName,
                operationType,
                id,
                accountNumber,
                ifscCode,
                status,
                operationTime,
            });

            // 返回成功响应
            res.status(200).json({ message: '处理成功' });
        } else {
            // 签名验证失败
            res.status(400).json({ message: '签名验证失败' });
        }
    } catch (error) {
        // 处理异常
        console.error('处理回调时发生错误:', error);
        res.status(500).json({ message: '服务器内部错误' });
    }
});