SpringBoot集成小米支付

小米支付

小米支付不像微信/支付宝支付那样的流程,而是直接由客户端APP进行发起的支付回调。

小米支付官网地址 https://dev.mi.com/distribute/doc/details?pId=1150#_21

个人支付案例git地址[微信支付/支付宝支付/华为支付/苹果支付/小米支付]:https://gitee.com/wazk2008/demo-pay

支付回调

yml配置

xiaomi:
  appid: 自己的appid
  app-secret: 自己的app对应的密钥

回调参数

@Data
public class PayMiCallbackRequest implements Serializable {

    // 游戏ID
    private String appId;

    // 开发商订单ID
    private String cpOrderId;

    // 开发商透传信息
    private String cpUserInfo;

    // 用户ID
    private String uid;

    // 游戏平台订单ID
    private String orderId;

    // 订单状态,TRADE_SUCCESS 代表成功
    private String orderStatus;

    // 支付金额,单位为分,即0.01米币
    private String payFee;

    // 商品代码
    private String productCode;

    // 商品名称
    private String productName;

    // 商品数量
    private String productCount;

    // 支付时间,格式 yyyy-MM-dd HH:mm:ss
    private String payTime;

    // 签名
    private String signature;

}

回调Controller

@RestController
@RequestMapping(value = "/pay")
@Slf4j
public class PayController {

    @Autowired
    private MiService miService;
  
    @PostMapping(value = "/miPayCallback")
    public Result<Object> payMiCallback (@RequestBody PayMiCallbackRequest payMiCallbackRequest) {
        try {
            log.info("小米支付回调接口开始执行,请求参数:{}", JSON.toJSON(payMiCallbackRequest));
            miService.payCallback(payMiCallbackRequest);
            log.info("小米支付回调接口执行成功,请求参数:{}", JSON.toJSON(payMiCallbackRequest));
            return Result.getSuccess();
        } catch (ResponseException e) {
            log.error("小米支付回调接口执行失败1,请求参数:{},异常原因:{}", JSON.toJSON(payMiCallbackRequest), e.getMessage());
            e.printStackTrace();
            return Result.getFail(e.getCode(), e.getMessage());
        } catch (Exception e) {
            log.error("小米支付回调接口执行失败2,请求参数:{},异常原因:{}", JSON.toJSON(payMiCallbackRequest), e.getMessage());
            e.printStackTrace();
            return Result.getFail(ResultCode.INTERNAL_SERVER_ERROR);
        }
    }
  
}

回调Service

@Service
public class MiService {

    @Value("${xiaomi.appid}")
    private String appId;
    @Value("${xiaomi.app-secret}")
    private String appSecret;
    @Autowired
    private RestTemplate restTemplate;


    private static final String MAC_NAME = "HmacSHA1";
    private static final String MI_QUERY_ORDER_URL = "https://mis.migc.xiaomi.com/api/biz/service/queryOrder.do";

    public void payCallback(PayMiCallbackRequest payMiCallbackRequest) {
        if (!"TRADE_SUCCESS".equals(payMiCallbackRequest.getOrderStatus())) {
            throw new ResponseException(ResultCode.MI_PAY_CALLBACK_ORDER_STATUS_ERROR);
        }
        if (!appId.equals(payMiCallbackRequest.getAppId())) {
            throw new ResponseException(ResultCode.MI_PAY_CALLBACK_APPID_ERROR);
        }
        // 校验签名
        String encryptText = generateEncryptText(convertOrderPayXiaomiCallbackRequestToMap(payMiCallbackRequest));
        String checkSignature = hmacSHA1Encrypt(encryptText, appSecret);
        if (!payMiCallbackRequest.getSignature().equals(checkSignature)) {
            // 签名校验失败
            throw new ResponseException(ResultCode.MI_PAY_CALLBACK_VALID_SIGN_ERROR);
        }

        // 请求小米支付服务端,获取小米支付状态
        boolean validMiRemoteService = validMiRemoteService(payMiCallbackRequest);
        if (!validMiRemoteService) {
            // 小米支付回调处理错误,请求小米校验订单状态错误
            throw new ResponseException(ResultCode.MI_PAY_CALLBACK_ORDER_STATUS_ERROR);
        }

        // todo 校验订单是否存在
        String outTradeNo = payMiCallbackRequest.getCpOrderId();

        // todo 校验订单是否已支付

        // todo 校验支付金额
        Double buyerAmount = Double.valueOf(payMiCallbackRequest.getPayFee());

        // todo 修改支付和订单的状态


    }

    // 生成验签字符串
    private String generateEncryptText (Map<String, String> map) {
        // 排序
        Set<String> keySet = map.keySet();
        String[] keyArray = keySet.toArray(new String[keySet.size()]);
        Arrays.sort(keyArray);
        StringBuilder sb = new StringBuilder();
        for (String k : keyArray) {
            sb.append("&").append(k).append("=").append(map.get(k).trim());
        }
        String sign = sb.toString();
        return sign.replaceFirst("&", "");
    }

    // 将 PayMiCallbackRequest 中的非空属性全部转为 map 格式
    private Map<String, String> convertOrderPayXiaomiCallbackRequestToMap (PayMiCallbackRequest payMiCallbackRequest){
        Class<? extends PayMiCallbackRequest> clazz = payMiCallbackRequest.getClass();
        Field[] fields = clazz.getDeclaredFields();
        Map<String, String> map = new HashMap<>();
        try {
            for (Field field : fields) {
                field.setAccessible(true);
                String fieldName = field.getName();
                Object fieldValue = field.get(payMiCallbackRequest);
                if (!Objects.isNull(fieldValue) && !StringUtils.isEmpty(fieldValue.toString()) && !"signature".equals(fieldName) && !"ivyTransactionId".equals(fieldName)) {
                    map.put(fieldName, URLDecoder.decode(fieldValue.toString(), CharEncoding.UTF_8));
                }
            }
        } catch (Exception e) {
            throw new ResponseException(ResultCode.MI_PAY_CALLBACK_PARAMS_CONVERT_ERROR);
        }
        return map;
    }

    // 使用 HMAC-SHA1 签名方法对对encryptText进行签名
    private String hmacSHA1Encrypt( String encryptText, String encryptKey) {
        byte[] data = encryptKey.getBytes(StandardCharsets.UTF_8);
        // 根据给定的字节数组构造一个密钥,第二参数指定一个密钥算法的名称
        SecretKey secretKey = new SecretKeySpec( data, MAC_NAME );
        // 生成一个指定 Mac 算法 的 Mac 对象
        Mac mac = null;
        try {
            mac = Mac.getInstance( MAC_NAME );
            // 用给定密钥初始化 Mac 对象
            mac.init( secretKey );
        } catch (Exception e) {
            // 生成签名错误
            throw new ResponseException(ResultCode.MI_PAY_CALLBACK_GENERATE_SIGN_ERROR);
        }
        byte[] text = encryptText.getBytes(StandardCharsets.UTF_8);
        // 完成 Mac 操作
        byte[] digest = mac.doFinal( text );
        StringBuilder sBuilder = bytesToHexString( digest );
        return sBuilder.toString();
    }

    private StringBuilder bytesToHexString( byte[] bytesArray ){
        if ( bytesArray == null ){
            return null;
        }
        StringBuilder sBuilder = new StringBuilder();
        for ( byte b : bytesArray ){
            String hv = String.format("%02x", b);
            sBuilder.append( hv );
        }
        return sBuilder;
    }

    // 向小米服务端发起校验
    private boolean validMiRemoteService (PayMiCallbackRequest payMiCallbackRequest) {
        // 请求小米支付服务端,获取小米支付状态
        String cpOrderId = payMiCallbackRequest.getCpOrderId();
        String uid = payMiCallbackRequest.getUid();
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("appId", appId);
        paramMap.put("cpOrderId", payMiCallbackRequest.getCpOrderId());
        paramMap.put("uid", uid);
        String newSignature = hmacSHA1Encrypt(generateEncryptText(paramMap), appSecret);
        String url = MI_QUERY_ORDER_URL + "?appId=" + appId + "&cpOrderId=" + cpOrderId + "&uid=" + uid + "&signature=" + newSignature;
        JSONObject miResponse = sendHttpRequest(url);
        return "TRADE_SUCCESS".equals(miResponse.getString("orderStatus"));
    }

    public JSONObject sendHttpRequest(String url) {
        HttpHeaders headers = new HttpHeaders();
        HttpEntity<String> httpEntity = new HttpEntity(headers);
        ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class, new Object[0]);
        String response = (String)exchange.getBody();
        return JSON.parseObject(response);
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

难过的风景

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值