package com.walker.pay.wechat; import com.walker.infrastructure.arguments.Variable; import com.walker.infrastructure.utils.KeyValue; import com.walker.infrastructure.utils.MD5; import com.walker.infrastructure.utils.StringUtils; import com.walker.pay.Order; import com.walker.pay.OrderGenerator; import com.walker.pay.OrderStatusQuery; import com.walker.pay.PayChannel; import com.walker.pay.PayContext; import com.walker.pay.PayStatus; import com.walker.pay.PayType; import com.walker.pay.ServiceProvider; import com.walker.pay.response.OrderStatusResponsePay; import com.walker.pay.support.SimplePayEngineProvider; import com.walker.pay.wechat.v2.AppOrderGenerator; import com.walker.pay.wechat.v2.H5OrderGenerator; import com.walker.pay.wechat.v2.RoutineOrderGenerator; import com.walker.pay.wechat.v2.ScanOrderGenerator; import com.walker.pay.wechat.v2.SignUtils; import com.walker.pay.wechat.v2.WechatV2PayContext; import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestTemplate; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; /** * 微信V2版本支付对接引擎实现。 * @author 时克英 * @date 2023-01-16 */ public class WechatV2PayEngineProvider extends SimplePayEngineProvider { private Map orderGeneratorMap = new HashMap<>(8); public WechatV2PayEngineProvider(RestTemplate restTemplate){ if(restTemplate == null){ throw new IllegalArgumentException("RestTemplate is required!"); } this.restTemplate = restTemplate; this.init(); } private void init(){ this.setServiceProvider(ServiceProvider.Wechat); this.setVersion("v2"); this.setPayChannel(PayChannel.ProviderDirect); ScanOrderGenerator scanOrderGenerator = new ScanOrderGenerator(this.restTemplate); AppOrderGenerator appOrderGenerator = new AppOrderGenerator(this.restTemplate); H5OrderGenerator h5OrderGenerator = new H5OrderGenerator(this.restTemplate); this.orderGeneratorMap.put(Constants.PAY_TYPE_NATIVE, scanOrderGenerator); this.orderGeneratorMap.put(Constants.PAY_TYPE_APP, appOrderGenerator); this.orderGeneratorMap.put(Constants.PAY_TYPE_H5, h5OrderGenerator); // 2023-09-15 RoutineOrderGenerator routineOrderGenerator = new RoutineOrderGenerator(this.restTemplate); this.orderGeneratorMap.put(Constants.PAY_TYPE_ROUTINE, routineOrderGenerator); } @Override protected String acquireProviderPayType(ServiceProvider serviceProvider, PayType payType, PayChannel payChannel) { if(payType == PayType.Scan){ return Constants.PAY_TYPE_NATIVE; } else if (payType == PayType.App){ return Constants.PAY_TYPE_APP; } else if (payType == PayType.H5) { return Constants.PAY_TYPE_H5; } else if (payType == PayType.Applet){ return Constants.PAY_TYPE_ROUTINE; }else { throw new UnsupportedOperationException("代码未实现第三方支付类型转换"); } } @Override protected OrderGenerator acquireOrderGenerator(String providerPayType, Order platformOrder, Map configuration) { return this.orderGeneratorMap.get(providerPayType); } @Override protected boolean verifySign(Object notifyData) throws Exception { // 暂时不验真签名 return true; } /*@Override protected void savePrepareOrder(Order platformOrder, ResponsePay responsePay) { if(this.logger.isDebugEnabled()){ logger.debug("保存订单:{}", platformOrder); } this.checkOrderCallback(); this.getOrderCallback().onOrderPrepare(platformOrder, responsePay); } @Override protected void onNotifyOrder(NotifyValue notifyValue) throws Exception { this.checkOrderCallback(); try { this.getOrderCallback().onOrderNotify(notifyValue); } catch (CallBackException e) { logger.error("订单支付通知回调错误:{}", notifyValue); throw new CallBackException("", e); } } private void checkOrderCallback(){ if(this.getOrderCallback() == null){ throw new IllegalArgumentException("OrderCallback 未配置,无法执行订单操作! payEngineProvider = " + this.getClass().getName()); } }*/ @Override protected PayContext acquirePayContext(String providerPayType, Order platformOrder, Map configuration) { WechatV2PayContext payContext = new WechatV2PayContext(); payContext.setConfigVariable(configuration.get(Constants.CONFIG_KEY_APP_ID)); payContext.setConfigVariable(configuration.get(Constants.CONFIG_KEY_MCH_ID)); payContext.setConfigVariable(configuration.get(Constants.CONFIG_KEY_API_KEY)); // 2023-09-15,添加小程序支付的应用参数 payContext.setConfigVariable(configuration.get(Constants.CONFIG_KEY_APP_ID_ROUTINE)); payContext.setConfigVariable(configuration.get(Constants.CONFIG_KEY_API_KEY_ROUTINE)); payContext.setConfigVariable(configuration.get(Constants.CONFIG_KEY_MCH_ID_ROUTINE)); return payContext; } @Override protected OrderStatusQuery acquireOrderStatusQuery(Order order) { throw new UnsupportedOperationException("没写代码呢!"); } @Override protected OrderStatusResponsePay invokeOrderStatus(OrderStatusQuery orderStatusQuery) { List> packageParams = new LinkedList<>(); packageParams.add(new KeyValue<>("appid", orderStatusQuery.getAppId())); packageParams.add(new KeyValue<>("mch_id", orderStatusQuery.getMerchantId())); packageParams.add(new KeyValue<>("nonce_str", StringUtils.generateRandomNumber(6))); packageParams.add(new KeyValue<>("out_trade_no", orderStatusQuery.getTradeNo())); packageParams.add(new KeyValue<>("sign_type", MD5.MD5_NAME)); // Map map = new HashMap<>(8); // map.put("appid", orderStatusQuery.getAppId()); String sign = SignUtils.getPackageSign(packageParams, orderStatusQuery.getApiKey()); packageParams.add(new KeyValue<>("sign", sign)); String request = SignUtils.toXml(packageParams); ResponseEntity entity = this.restTemplate.postForEntity(Constants.URL_SEARCH_V2, request, String.class); if(entity == null){ throw new RuntimeException("调用'微信H5'查询订单返回空数据, trade_no = " + orderStatusQuery.getTradeNo()); } Map responseMap = SignUtils.decodeXml(entity.getBody()); if(responseMap == null){ throw new RuntimeException("调用'微信H5'查询订单返回数据转换为空: SignUtils.decodeXml() == null"); } if(logger.isDebugEnabled()){ logger.debug(responseMap.toString()); } OrderStatusResponsePay responsePay = new OrderStatusResponsePay(); if(responseMap.get("return_code").equals("FAIL")){ responsePay.setStatus(false); responsePay.setMessage(responseMap.get("return_msg")); } else if(responseMap.get("result_code").equals("FAIL")){ responsePay.setStatus(false); responsePay.setMessage(responseMap.get("err_code_des")); logger.error("微信查询订单调用成功,但返回错误结果:" + responseMap.get("err_code")); } else { responsePay.setStatus(true); String tradeState = responseMap.get("trade_state"); if(tradeState.equals(Constants.TRADE_STATE_CLOSED)){ responsePay.setPayStatus(PayStatus.Closed); responsePay.setMessage("已关闭"); } else if(tradeState.equals(Constants.TRADE_STATE_USERPAYING)){ responsePay.setPayStatus(PayStatus.Paying); responsePay.setMessage("正在支付中"); } else if (tradeState.equals(Constants.TRADE_STATE_NOTPAY)){ responsePay.setPayStatus(PayStatus.NotPay); responsePay.setMessage("未支付"); } else if(tradeState.equals(Constants.TRADE_STATE_REFUND)){ responsePay.setPayStatus(PayStatus.Refund); responsePay.setMessage("转入退款"); } else if(tradeState.equals(Constants.TRADE_STATE_PAYERROR)){ responsePay.setPayStatus(PayStatus.Error); responsePay.setMessage("支付失败"); } else if(tradeState.equals(Constants.TRADE_STATE_REVOKED)){ responsePay.setPayStatus(PayStatus.Error); responsePay.setMessage("已撤销(刷卡支付) "); } else if(tradeState.equals(Constants.TRADE_STATE_SUCCESS)){ responsePay.setPayStatus(PayStatus.Success); responsePay.setUserPayMoney(Long.parseLong(responseMap.get("cash_fee"))); responsePay.setTotalMoney(Long.parseLong(responseMap.get("total_fee"))); responsePay.setPaySuccessTime(responseMap.get("time_end")); responsePay.setOrderId(responseMap.get("out_trade_no")); responsePay.setTradeNo(responseMap.get("transaction_id")); responsePay.setAppId(orderStatusQuery.getAppId()); responsePay.setMerchantId(orderStatusQuery.getMerchantId()); responsePay.setProviderPayType(responseMap.get("trade_type")); } else { throw new UnsupportedOperationException("不支持微信返回的支付类型:" + tradeState); } } return responsePay; } public void setRestTemplate(RestTemplate restTemplate) { this.restTemplate = restTemplate; } private RestTemplate restTemplate; @Override public String generateNotifyResponse(boolean success) { if(success){ return SignUtils.toXmlNotifyStatus(Constants.CODE_SUCCESS, Constants.CODE_OK); } else { return SignUtils.toXmlNotifyStatus(Constants.CODE_FAIL, Constants.CODE_FAIL); } } }