Puntos de pago de WeChat (5) - Notificación de devolución de llamada

Directorio de artículos


1. Introducción

Dirección del código: https://github.com/xm646236438/wechat_pay_score/tree/master
El
código y la lógica específicos de SpringBoot se pueden ver en los puntos de pago de WeChat (1): creación de órdenes de pago y el
blog anterior en GitHub : puntos de pago de Wechat (4) -Cancelar pago de subpedidos

2. Código

public ResponseEntity payScoreCallbackNotification(HttpServletRequest request) {
    
    
        try {
    
    
            ServletInputStream servletInputStream = request.getInputStream();
            int contentLength = request.getContentLength();
            byte[] callBackInBytes = new byte[contentLength];
            servletInputStream.read(callBackInBytes, 0, contentLength);
            String callBackIn = new String(callBackInBytes, "UTF-8");
            // 模拟确认订单回调通知API
//            String callBackIn = "{\"id\":\"123\",\"create_time\":\"2020-11-02T16:31:35+08:00\",\"resource_type\":\"encrypt-resource\",\"event_type\":\"PAYSCORE.USER_CONFIRM\",\"summary\":\"微信支付分服务订单用户已确认\",\"resource\":{\"original_type\":\"payscore\",\"algorithm\":\"AEAD_AES_256_GCM\",\"ciphertext\":\"1111111111==\",\"associated_data\":\"payscore\",\"nonce\":\"12321321\"}}";
            // 模拟支付成功回调通知API
//            String callBackIn = "{\"id\":\"123\",\"create_time\":\"2020-11-02T16:31:35+08:00\",\"resource_type\":\"encrypt-resource\",\"event_type\":\"PAYSCORE.USER_PAID\",\"summary\":\"微信支付分服务订单支付成功\",\"resource\":{\"original_type\":\"payscore\",\"algorithm\":\"AEAD_AES_256_GCM\",\"ciphertext\":\"1111111111==\",\"associated_data\":\"payscore\",\"nonce\":\"12321321\"}}";

            log.info("【微信支付分免密支付回调】:" + callBackIn);


            JSONObject notifyIn = JSONObject.parseObject(callBackIn);
            if (notifyIn == null) {
    
    
                log.error("参数不正确,反序列化失败");
                return new ResponseEntity(HttpStatus.EXPECTATION_FAILED);
            }

            //解密回调信息
            JSONObject resource = notifyIn.getJSONObject("resource");
            byte[] key = (mchKeyVVV).getBytes("UTF-8");
            ApiV3Util aesUtil = new ApiV3Util(key);
            String decryptToString = aesUtil.decryptToString(resource.getString("associated_data").getBytes("UTF-8"), resource.getString("nonce").getBytes("UTF-8"), resource.getString("ciphertext"));

            if (StringUtils.isEmpty(decryptToString)) {
    
    
                return new ResponseEntity(HttpStatus.EXPECTATION_FAILED);
            }
            log.info("【支付分支付回调解密结果:】" + decryptToString);


            // 用户确认成功
            if ("PAYSCORE.USER_CONFIRM".equals(notifyIn.get("event_type"))) {
    
    
                log.info("用户确认成功");
                // 处理业务逻辑
            }
            // 支付成功
            if ("PAYSCORE.USER_PAID".equals(notifyIn.get("event_type"))) {
    
    
                log.info("用户支付成功");
                // 处理业务逻辑
            }
            return new ResponseEntity(HttpStatus.OK);
        } catch (Exception e) {
    
    
            log.error("微信支付回调处理异常," + e.toString());
            return new ResponseEntity(HttpStatus.EXPECTATION_FAILED);
        }
    }
package com.tomorrow.wechat_pay_score.util.wechart;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

/**
 * @author Tomorrow
 * @date 2020/6/21 17:09
 */
public class ApiV3Util {
    
    
    static final int KEY_LENGTH_BYTE = 32;
    static final int TAG_LENGTH_BIT = 128;
    private final byte[] aesKey;


    public ApiV3Util(byte[] key) {
    
    
        if (key.length != KEY_LENGTH_BYTE) {
    
    
            throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节");
        }
        this.aesKey = key;
    }

    public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext)
            throws GeneralSecurityException, IOException {
    
    
        try {
    
    
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

            SecretKeySpec key = new SecretKeySpec(aesKey, "AES");
            GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);

            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            cipher.updateAAD(associatedData);

            return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
    
    
            throw new IllegalStateException(e);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
    
    
            throw new IllegalArgumentException(e);
        }
    }
}

Inserte la descripción de la imagen aquí
Inserte la descripción de la imagen aquí

3, notas

1. La dirección de devolución de llamada debe ser HTTPS, nombre de dominio, no IP, IP aceptará menos de
2. API de notificación de devolución de llamada de confirmación de pedido (el usuario volverá a llamar después de confirmar la autorización)
3. API de notificación de devolución de llamada de éxito de pago (después de la llamada completado para pagar el suborden Se ajustará después de la API)
4. Porque hay mucha información clave, por lo que hay más mosaicos
5. Cómo descifrar si es un comerciante múltiple: 1. Sondeo y descifrado , 2. Realice la operación en la notificación_url que creó el pedido antes y realice el estilo Restfule, por ejemplo: https: // **** / {orderNo} / test, intercept orderNo para distinguir
Inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/qq_38637558/article/details/109452822
Recomendado
Clasificación