微信小程序支付 JSAPI v3—下单、回调

微信小程序支付 JSAPI v3—下单、回调微信支付JSAPIv3一、1.VO[ValueObject,ViewObject]页面中展示的对象,只传输“需要”展示的内容,保证数据的安全。2.DO[DomainObject]DO为数据库表结构完全一致的对象3.PO[PersistentObject]PO是数据库表映射后的对象,数据库表到java对象字段的映射。如1:我需要的数据有订单信息和库存信息,关联查询后的数据,存储有订单、库存信息数据,即PO。(一个对象存储多张表映射后的结果



微信小程序支付 JSAPI v3—下单、回调


一、接入前准备


1. 微信支付文档中心

@ 接入准备: https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_1.shtml
@ 小程序支付API列表: https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_3.shtml

2. pom.xml加入依赖
<dependency>
    <groupId>com.github.binarywang</groupId>
    <artifactId>weixin-java-pay</artifactId>
    <version>4.3.0</version>
</dependency>
3. application.yml
#微信支付参数相关
wx:
  pay:
    app_id:  #微信公众号或者小程序等的appid
    mch_id:  #微信支付商户号
    Api_V3_Key:  # api v3支付秘钥 #微信支付商户密钥
    # subAppId: #服务商模式下的子商户公众账号ID
    # subMchId: #服务商模式下的子商户号
    private_key_path:  # p12证书的位置,可以指定绝对路径,也可以指定类路径(以classpath:开头)
    private_cert_path:  # p12证书的位置,可以指定绝对路径,也可以指定类路径(以classpath:开头)
    notify_url:  # 支付成功回调

二、编码

1. 编写配置WxPayProperties读取配置
@Component
@Data
@ConfigurationProperties(prefix = "wx.pay")
public class WxPayProperties { 
   

    /** * 设置微信公众号或者小程序等的appid */
    private String appId;

    /** * 微信支付商户号 */
    private String mchId;

    /** * 微信支付商户密钥 */
    private String apiV3Key;

    /** * 服务商模式下的子商户公众账号ID,普通模式请不要配置,请在配置文件中将对应项删除 */
    private String subAppId;

    /** * 服务商模式下的子商户号,普通模式请不要配置,最好是请在配置文件中将对应项删除 */
    private String subMchId;

    /** * apiclient_key.pem文件的绝对路径,或者如果放在项目中,请以classpath:开头指定 */
    private String privateKeyPath;

    /** * apiclient_key.pem文件的绝对路径,或者如果放在项目中,请以classpath:开头指定 */
    private String privateCertPath;

    /** * 支付成功回调地址:v3版本,必须是https */
    private String notifyUrl;
}
2. 初始化微信支付相关配置参数
/** * 微信支付配置 */
@Configuration
@ConditionalOnClass(WxPayService.class)
@AllArgsConstructor
public class AlarmWxPayConfig { 
   

    @Autowired
    private WxPayProperties properties;

    /** * 初始化微信支付相关配置参数 * @return */
    @Bean
    @ConditionalOnMissingBean
    public WxPayService wxService() { 
   
    
        WxPayConfig payConfig = new WxPayConfig();
        payConfig.setAppId(StringUtils.trimToNull(this.properties.getAppId()));
        payConfig.setMchId(StringUtils.trimToNull(this.properties.getMchId()));
        payConfig.setSubAppId(StringUtils.trimToNull(this.properties.getSubAppId()));
        payConfig.setSubMchId(StringUtils.trimToNull(this.properties.getSubMchId()));
        payConfig.setPrivateCertPath(StringUtils.trimToNull(this.properties.getPrivateCertPath()));
        payConfig.setPrivateKeyPath(StringUtils.trimToNull(this.properties.getPrivateKeyPath()));
        payConfig.setApiV3Key(StringUtils.trimToNull(this.properties.getApiV3Key()));

        // 可以指定是否使用沙箱环境
        payConfig.setUseSandboxEnv(false);
        WxPayService wxPayService = new WxPayServiceImpl();
        wxPayService.setConfig(payConfig);
        return wxPayService;
    }
}
3. 下单

@ JSAPI下单
在这里插入图片描述
@ 请求参数(必填)

{ 
   
	"appid": "",                   //【应用ID】必填
	"mchid": "",                   //【直连商户号】必填
	"description ": "",            //【商品描述】必填
	"out_trade_no": "",            //【商户订单号】必填
	"notify_url ": "",            //【通知地址】必填
	"amount": { 
                       //【订单金额】必填
		"total": "",               //【总金额】必填
	},
	"payer": { 
                        //【支付者】
		"openid": ""               //【用户标识】必填
	},
	"detail": { 
                       //【优惠功能】
		"invoice_id": "",
		"goods_detail":{ 
              //【单品列表】
			"merchant_goods_id": "",//【商户侧商品编码】必填
			"quantity": "",         //【商品数量】必填
			"unit_price": ""        //【商品单价】必填
		}
	},
	"scene_info": { 
                   //【场景信息】
		"payer_client_ip": "",     //【用户终端IP】必填
		"store_info": { 
               //【商户门店信息】
		"id": "",              //【门店编号】必填
		}
	},
}

@ JSAPI 下单—-生成预支付交易单—-返回小程序调起支付API—–必要参数

    /** * * JSAPI 下单----生成预支付交易单----返回小程序调起支付API-----必要参数 * * 填入必填项 * * @return */
    public WxPayUnifiedOrderV3Result.JsapiResult prePay("传入业务数据,填充") { 
   
        WxPayUnifiedOrderV3Request v3Request = new WxPayUnifiedOrderV3Request();

        ArrayList<WxPayUnifiedOrderV3Request.GoodsDetail> goodsDetails = new ArrayList<>();
        goodsDetails.add(new WxPayUnifiedOrderV3Request.GoodsDetail() { 
   
        }
                .setMerchantGoodsId("")
                .setUnitPrice(0).setQuantity(0));

        v3Request.setAppid("")
                .setMchid("")
                .setNotifyUrl("")
                .setDescription("")
                .setOutTradeNo("")
                .setAmount(new WxPayUnifiedOrderV3Request.Amount() { 
   
                }.setTotal(0))
                .setPayer(new WxPayUnifiedOrderV3Request.Payer() { 
   
                }.setOpenid(""))
                .setDetail(new WxPayUnifiedOrderV3Request.Discount() { 
   
                }.setInvoiceId("").setGoodsDetails(goodsDetails))
                .setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo() { 
   
                }
                        .setStoreInfo(new WxPayUnifiedOrderV3Request.StoreInfo() { 
   
                        }.setId("")).setPayerClientIp(""));

        JsapiResult jsapiResult = null;
        try { 
   
            jsapiResult = this.wxPayService.createOrderV3(TradeTypeEnum.valueOf("JSAPI"), v3Request);
        } catch (WxPayException e) { 
   
            e.printStackTrace();
            log.info("JSAPI 下单:{}",e.getLocalizedMessage());
        }
        return jsapiResult;
    }
4. 页面支付成功,回调

@ 页面支付成功,查询微信后台订单状态,更新商户平台订单状态

/** * 微信查询订单,参数两者使用其一 * * @param orderNo 商户订单号 * @param transactionId 微信交易流水号 * @return Result */
    public Result<?> wxQueryPay(String orderNo, String transactionId) { 
   
        WxPayOrderQueryV3Result v3Result = null;
        try { 
   
            v3Result = this.wxPayService.queryOrderV3(transactionId, orderNo);
        } catch (WxPayException e) { 
   
            log.error("查询微信后台订单失败:{}", e.getMessage());
            return Result.restResult(6001, e.getMessage(), null);
        }
        // 根据业务需要,更新商户平台订单状态
        if(v3Result.getPayState.equels("SUCCES")){ 
   
        // 业务需求
        }
        return Result.ok();
    }

@ 页面支付成功回调问题
在这里插入图片描述

5. notify_url,回调

@ 解决,

  1. 页面支付成功调用API列表—–查询订单,更新
  2. 生成预订单时提供的notify_url,微信后台判定订单支付成功,主动发起访问商户平台
  3. 采用定时任务,轮询商户订单未支付订单,调用查询订单,更新 (本文未提供)
    /** * 获取回调请求头:签名相关 * * @param request HttpServletRequest * @return SignatureHeader */
    public SignatureHeader getRequestHeader(HttpServletRequest request) { 
   
        // 获取通知签名
        String signature = request.getHeader("wechatpay-signature");
        String nonce = request.getHeader("wechatpay-nonce");
        String serial = request.getHeader("wechatpay-serial");
        String timestamp = request.getHeader("wechatpay-timestamp");

        SignatureHeader signatureHeader = new SignatureHeader();
        signatureHeader.setSignature(signature);
        signatureHeader.setNonce(nonce);
        signatureHeader.setSerial(serial);
        signatureHeader.setTimeStamp(timestamp);
        return signatureHeader;
    }
    /** * 微信支付回调 * * @param jsonData String * @param request HttpServletRequest * @param response HttpServletResponse * @return JSONObject */
     @PostMapping("/wxNotifyUrl")
    public JSONObject wxNotifyUrl(@RequestBody String jsonData, HttpServletRequest request, HttpServletResponse response) { 
   

        JSONObject wxPayResult = new JSONObject();
        if (lock.tryLock()) { 
   
            // 支付成功结果通知
            OriginNotifyResponse notifyResponse = JSONUtil.toBean(jsonData, OriginNotifyResponse.class);
            WxPayOrderNotifyV3Result v3Result = null;
            try { 
   
                v3Result=wxPayService.parseOrderNotifyV3Result(this.jsonStrSort(notifyResponse),this.getRequestHeader(request));

                //解密后的数据
                WxPayOrderNotifyV3Result.DecryptNotifyResult result = v3Result.getResult();

                // 注意:微信会通知多次,因此需判断此订单
                LambdaQueryWrapper<WxPayOrder> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(wxPayOrder::getOutTradeNo, result.getOutTradeNo());
                WxPayOrder wxPayOrder = this.wxPayOrderMapper.selectOne(queryWrapper);
				// 0:未支付,1:已支付
				if(wxPayOrder.getPayState == 0){ 
   
				     //根据业务需要,更新商户平台订单状态
				}
      
                //通知应答:接收成功:HTTP应答状态码需返回200或204,无需返回应答报文。
                response.setStatus(HttpServletResponse.SC_OK);
                return null;
            } catch (WxPayException e) { 
   
                e.printStackTrace();
                log.error("支付回调失败:{}", e.getLocalizedMessage());
                // 通知应答:HTTP应答状态码需返回5XX或4XX,同时需返回应答报文
                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                wxPayResult.putOpt("code", "FAIL");
                wxPayResult.putOpt("message", "失败");
                return wxPayResult;
            } finally { 
   
                lock.unlock();
            }
        }
        // 通知应答码:HTTP应答状态码需返回5XX或4XX,同时需返回应答报文
        response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        wxPayResult.putOpt("code", "FAIL");
        wxPayResult.putOpt("message", "失败");
        return wxPayResult;
    }

在这里插入图片描述
@ 从图上所说,有点模糊,翻译过来:就是需要按照API列表中所示的顺序,排序才能正确验签。,但实际body中接收到jsonData的顺序不是如下图所示,因此需重新排序。
在这里插入图片描述
@最简陋的方式排序。

    /** * 请求报文:按官方接口示例键值 --- 排序(必须) * 官方文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_5.shtml, * https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_1.shtml * 《微信支付API v3签名验证》 中注意:应答主体(response Body),需要按照接口返回的顺序进行验签,错误的顺序将导致验签失败。 * * @param originNotifyResponse OriginNotifyResponse * @return String */
    private String jsonStrSort(OriginNotifyResponse originNotifyResponse) { 
   

        Map<String, Object> jsonSort = new LinkedHashMap<>();
        jsonSort.put("id", originNotifyResponse.getId());
        jsonSort.put("create_time", originNotifyResponse.getCreateTime());
        jsonSort.put("resource_type", originNotifyResponse.getResourceType());
        jsonSort.put("event_type", originNotifyResponse.getEventType());
        jsonSort.put("summary", originNotifyResponse.getSummary());
        Map<String, Object> resource = new LinkedHashMap();
        resource.put("original_type", originNotifyResponse.getResource().getOriginalType());
        resource.put("algorithm", originNotifyResponse.getResource().getAlgorithm());
        resource.put("ciphertext", originNotifyResponse.getResource().getCiphertext());
        resource.put("associated_data", originNotifyResponse.getResource().getAssociatedData());
        resource.put("nonce", originNotifyResponse.getResource().getNonce());
        jsonSort.put("resource", resource);
        return JSONUtil.toJsonStr(jsonSort);
    }

三、随笔而已

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/39335.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注