Pago del subprograma java WeChat: devolución de llamada (Jsapi-APIv3)

1. Preparación antes del acceso, preparar según este documento

       Preparación:  Preparación antes de acceder - Mini Program Payment | WeChat Payment Merchant Platform Documentation Center

Puede obtenerlo cuando esté listo ( parámetros requeridos en el segundo punto ):
        parámetro 1 ID del comerciante: xxxxxx (todos los números)

        Parámetro 2 Merchant APIV3 key apiV3key: xxxxxxx (32 cadenas alfanuméricas en mayúsculas y minúsculas, preparadas por el propio desarrollador)

        Parámetro 3 Número de serie del certificado de comerciante mercantilSerialNumber: xxxxx

            Método de visualización: dónde encontrar el número de serie del certificado de pago de WeChat (dónde encontrar el número de serie del certificado de comerciante v3)-Li Fei SEO

        Parámetro 4 Ruta de clave privada de Merchant API privateKeyPath: ruta de archivo de apiclient_key.pem

--------------------- Orden de pago nativo (código QR), los primeros cuatro son suficientes, 5 es para pago Jsapi ------ ---- ------
        Parámetro 5 Ruta del certificado de la plataforma de pago WeChat secreta nacional (archivo X509) wechatPayCertificatePath: wechatpay_xxxx.pem()

                He estado buscando este archivo durante mucho tiempo y finalmente probé la herramienta a continuación para descargarlo usando GitHub: wechatpay-apiv3/CertificateDownloader: Herramienta de descarga de línea de comando del certificado de la plataforma APIv3 de pago de Java WeChat
 

2. Acceso al código de pago

Documentación del sitio web oficial de pago: https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7

Wechat hizo un paquete maven para apiv3, y finalmente lo parecía. Como resultado, descubrí que, aunque el ejemplo de pago nativo está completo, no sé cómo dar los parámetros 1-5 en el primer punto. Los parámetros jsapi son los mismos, soy El mosaico de cosas se unió. documentación del paquete maven

-

Publique un ejemplo de llamada de jsapi, basta con invocar el pago en el lado del applet, esto se basa en el ejemplo oficial:

import com.alibaba.fastjson.JSONObject;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAConfig;
import com.wechat.pay.java.core.exception.HttpException;
import com.wechat.pay.java.core.exception.MalformedMessageException;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.CloseOrderRequest;
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
import com.wechat.pay.java.service.payments.jsapi.model.QueryOrderByIdRequest;
import com.wechat.pay.java.service.payments.jsapi.model.QueryOrderByOutTradeNoRequest;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class JsapiDemoService {
    public static String merchantId = "1xxxx";
//    public static String privateKeyPath = "";
    public static String merchantSerialNumber = "xxxx";
    public static String wechatPayCertificatePath = "";

    public static com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension service;

    public static PrepayWithRequestPaymentResponse createWxJsapOrder(String certificatePath, String keyPath, JsapiReq cliReq) {
        String method = Thread.currentThread().getStackTrace()[1].getMethodName();

        // 使用自动更新平台证书的RSA配置
        // 一个商户号只能初始化一个配置,否则会因为重复的下载任务报错
        Config config =
                new RSAConfig.Builder()
                        .merchantId(merchantId)
                        // 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名
                        .privateKeyFromPath(keyPath)
                        .merchantSerialNumber(merchantSerialNumber)
                        .wechatPayCertificatesFromPath(certificatePath)
                        .build();
        // 构建service
        JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(config).build();
        // request.setXxx(val)设置所需参数,具体参数可见Request定义
        PrepayRequest request = new PrepayRequest();

        Payer payer = new Payer();
        payer.setOpenid(cliReq.getOpenid());
        request.setPayer(payer);

        Amount amount = new Amount();
        amount.setTotal(10);//订单总金额,单位为分
        request.setAmount(amount);

        request.setAppid("wxddddxxxxxx");
        request.setMchid("1xxxx");
        request.setDescription("ms");
        request.setNotifyUrl("https://xxx.net/callBackr");
        //request.setOutTradeNo("out_trade_no_1");
        request.setOutTradeNo(cliReq.getOutTradeNo());
        // 调用下单方法,得到应答
        try {
            // ... 调用接口
            PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request);
            // 使用微信扫描 code_url 对应的二维码,即可体验Native支付
            System.out.println(JSONObject.toJSON(response));
            return response;
        } catch (HttpException e) { // 发送HTTP请求失败
            // 调用e.getHttpRequest()获取请求打印日志或上报监控,更多方法见HttpException定义
            log.error(method, e);
        } catch (ServiceException e) { // 服务返回状态小于200或大于等于300,例如500
            // 调用e.getResponseBody()获取返回体打印日志或上报监控,更多方法见ServiceException定义
            log.error(method, e);
        } catch (MalformedMessageException e) { // 服务返回成功,返回体类型不合法,或者解析返回体失败
            // 调用e.getMessage()获取信息打印日志或上报监控,更多方法见MalformedMessageException定义
            log.error(method, e);
        } catch (Exception e) { // 服务返回成功,返回体类型不合法,或者解析返回体失败
            // 调用e.getMessage()获取信息打印日志或上报监控,更多方法见MalformedMessageException定义
            log.error(method, e);
        }
        return new PrepayWithRequestPaymentResponse();
    }

    /** 关闭订单 */
    public static void closeOrder() {

        CloseOrderRequest request = new CloseOrderRequest();
        // 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
        // 调用接口
        service.closeOrder(request);
    }
    /** JSAPI支付下单,并返回JSAPI调起支付数据 */
    public static PrepayWithRequestPaymentResponse prepayWithRequestPayment() {
        PrepayRequest request = new PrepayRequest();
        // 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
        // 调用接口
        return service.prepayWithRequestPayment(request);
    }
    /** 微信支付订单号查询订单 */
    public static Transaction queryOrderById() {

        QueryOrderByIdRequest request = new QueryOrderByIdRequest();
        // 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
        // 调用接口
        return service.queryOrderById(request);
    }
    /** 商户订单号查询订单 */
    public static Transaction queryOrderByOutTradeNo() {

        QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
        // 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
        // 调用接口
        return service.queryOrderByOutTradeNo(request);
    }
}

Complete la definición de JsapiReq

public class JsapiReq implements Serializable {
    private static final long serialVersionUID = 1L;
    private String openid;
    private String outTradeNo;
}

 Recuerde también cómo leer la ruta del archivo de recursos en java/spring/srpingboot

        String userDir = System.getProperty("user.dir");
        String certificatePath = userDir + "/src/main/resources/cert/wechatpay_xxx.pem";
        String keyPath = userDir + "/src/main/resources/cert/apiclient_xxxxkey.pem";

3. Devolución de llamada de pago

Devolución de llamada de pago: documentación del sitio web oficial: WeChat Payment: documentación del desarrollador

el código

    @PostMapping(value = "/callBack")
    public Map<String, String> callBack(@RequestBody JSONObject jsonObject) {
        String method = Thread.currentThread().getStackTrace()[1].getMethodName();

        try {
            String key = WxNativePayProxy.getWxV3Key();
            String json = jsonObject.toString();
            String associated_data = (String) JSONUtil.getByPath(JSONUtil.parse(json), "resource.associated_data");
            String ciphertext = (String) JSONUtil.getByPath(JSONUtil.parse(json), "resource.ciphertext");
            String nonce = (String) JSONUtil.getByPath(JSONUtil.parse(json), "resource.nonce");

            String decryptData = new AesUtil(key.getBytes(StandardCharsets.UTF_8)).decryptToString(associated_data.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);
            //验签成功
            JSONObject decryptDataObj = JSONObject.parseObject(decryptData, JSONObject.class);            
            //decryptDataObj 为解码后的obj,其内容如下。之后便是验签成功后的业务处理
            //{
            //	"sp_appid": "wx8888888888888888",
            //	"sp_mchid": "1230000109",
            //	"sub_appid": "wxd678efh567hg6999",
            //	"sub_mchid": "1900000109",
            //	"out_trade_no": "1217752501201407033233368018",
            //	"trade_state_desc": "支付成功",
            //	"trade_type": "MICROPAY",
            //	"attach": "自定义数据",
            //	"transaction_id": "1217752501201407033233368018",
            //	"trade_state": "SUCCESS",
            //	"bank_type": "CMC",
            //	"success_time": "2018-06-08T10:34:56+08:00",
            //    ...
            //	"payer": {
            //		"openid": "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"
            //	},
            //	"scene_info": {
            //		"device_id": "013467007045764"
            //	}
            //}
        }catch (Exception e){
            log.info("{} ,parms{}, 异常:", method, jsonObject.toJSONString(), e);
        }

        Map<String, String> res = new HashMap<>();
        res.put("code", "SUCCESS");
        res.put("message", "成功");
        return res;
    }

Entre ellos, la clase de herramienta de decodificación de WeChat es útil: AesUtil

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

public class AesUtil{

    static final int KEY_LENGTH_BYTE = 32;
    static final int TAG_LENGTH_BIT = 128;
    private final byte[] aesKey;

    public AesUtil(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);
        }
    }
}

Supongo que te gusta

Origin blog.csdn.net/tiantiannianni/article/details/130476543
Recomendado
Clasificación