The previous article introduced how to realize the unified order of wechat, but in actual production, it is not recommended to use it directly. Code in development needs to be portable and low coupling. Therefore, the special reconstruction of the wechat payment code, hoping to provide some help to interested friends.
Refactoring steps
Configuring class injection
1. Create an entity class to encapsulate the configuration parameters of wechat public platform
@Data
public class WCPConfigParams {
// Public id
private String appId;
/ / app key
private String appSecret;
/ / merchants
private String muchId;
/ / API keys
private String apiKey;
// Public account register domain name
private String registerDomain;
// jsAPI payment directory
private String jsapiPaymentAuthDir;
// js secure domain name
private String jsDomain;
// Web page authorization domain name
private String webAuthDomain;
// Certificate directory
private String apiclientCert;
// Pay the callback address
private String notifyUrl;
// Refund callback address
private String notifyUrlRefund;
}
Copy the code
2. Create an properties file to save the configuration information
APP_ID= # interface key wcp.APPSECRET= # wechat Pay merchant number wcp.MCH_ID= # API key wcp.API_KEY= # registered domain name Wcp.register_domain = # JSAPI Payment authorization directory wcp.jsapi_payment_auth_dir = # JS interface security domain name wcp.js_domain = # Web authorization domain name wcp.web_auth_domain = # Certificate path wcp.APICLIENT_CERT= # Callback address for receiving notification of wechat pay result asynchronously. Notification URL must be accessible to the external network and cannot carry parameter Wcp. NOTIFY_URL= # callback address for receiving notification of wechat Pay refund result asynchronously. Note The notification URL must be accessible to the Internet, and no parameters are allowed. If notifY_URL is passed, the callback address configured on the merchant platform will not take effect. wcp.NOTIFY_URL_REFUND=Copy the code
3. Create a configuration class and inject the bean of the configuration parameters
@Configuration
@PropertySource("classpath:config/wechat-pay.properties")
public class WCPConfig {
@Autowired
private Environment env;
@Bean
public WCPConfigParams wcpConfigParams(a) {
WCPConfigParams params = new WCPConfigParams();
params.setAppId(env.getProperty("wcp.APP_ID"));
params.setAppSecret(env.getProperty("wcp.APPSECRET"));
params.setMuchId(env.getProperty("wcp.MCH_ID"));
params.setApiKey(env.getProperty("wcp.API_KEY"));
params.setRegisterDomain(env.getProperty("wcp.REGISTER_DOMAIN"));
params.setJsapiPaymentAuthDir(env.getProperty("wcp.JSAPI_PAYMENT_AUTH_DIR"));
params.setJsDomain(env.getProperty("wcp.JS_DOMAIN"));
params.setWebAuthDomain(env.getProperty("wcp.webAuthDomain"));
params.setApiclientCert(env.getProperty("wcp.APICLIENT_CERT"));
params.setNotifyUrl(env.getProperty("wcp.NOTIFY_URL"));
params.setNotifyUrlRefund(env.getProperty("wcp.NOTIFY_URL_REFUND"));
returnparams; }}Copy the code
4. WXPayConfig in SDK is changed from abstract class to interface
interface WXPayConfig {
/** * Get App ID */
String getAppID(a);
// ...
/** * Obtain merchant certificate content */
InputStream getCertStream(a);
/** * HTTP(S) connection timeout in milliseconds */
default int getHttpConnectTimeoutMs(a) {
return 6 * 1000;
}
// ...
}
Copy the code
5. Implement the interface
public class WXPayConfigImpl implements WXPayConfig {
private WCPConfigParams wcpConfigParams;
private byte[] certData;
public WXPayConfigImpl(WCPConfigParams wcpConfigParams) throws IOException {
this.wcpConfigParams = wcpConfigParams;
String certPath = wcpConfigParams.getApiclientCert();
File file = new File(certPath);
InputStream certStream = new FileInputStream(file);
this.certData = new byte[(int)file.length()];
certStream.read(this.certData);
certStream.close();
}
public WXPayConfigImpl(a) {}
public void setWcpConfigParams(WCPConfigParams wcpConfigParams) {
this.wcpConfigParams = wcpConfigParams;
}
@Override
public String getAppID(a) {
return wcpConfigParams.getAppId();
}
// ...
}
Copy the code
6. Configure the newly injected bean in the class
@Bean
@DependsOn(value = "wcpConfigParams")
public WXPayConfigImpl wxPayConfigImpl(a) {
WXPayConfigImpl wxPayConfigImpl = new WXPayConfigImpl();
wxPayConfigImpl.setWcpConfigParams(wcpConfigParams());
return wxPayConfigImpl;
}
@Bean(name = "wxPayDefault")
@DependsOn(value = "wxPayConfigImpl")
public WXPay wxPayDefault(a) throws Exception {
WXPay wxPay = new WXPay(wxPayConfigImpl());
return wxPay;
}
Copy the code
Although Spring is executed from top to bottom, it is recommended to add bean dependencies
Wechat payment function development
Unified order
1. Added the entity class of unified order
@Data
public class UnifiedOrderRequestEntity {
/** * public ID */
private String appid;
/** * Merchant number */
@JSONField(name = "mch_id")
private String mchId;
/**
* 设备号
*/
@JSONField(name = "device_info")
private String deviceInfo;
// ...
}
Copy the code
2. Added the wechat Payment backend tool class
@Component
public class WCPBackendUtil {
@Autowired
@Qualifier("wxPayDefault")
private WXPay wxPayDefault;
@Autowired
private WCPConfigParams wcpConfigParams;
@Autowired
private WPPSignatureUtil wppSignatureUtil;
@Autowired
private WPPBackendUtil wppBackendUtil;
/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **@paramOpenid user's unique id * in the public account@paramTradeType tradeType *@paramPrice price *@paramProductDesc Product description *@paramTerminalIP terminalIP *@paramRequestUrl Specifies the url of the request source *@returnReturns the map */ of the JS validation parameter
public Map<String, Object> unifiedorder(String openid, String tradeType, String price, String productDesc, String terminalIP, String requestUrl) {
try {
UnifiedOrderRequestEntity requestEntity = new UnifiedOrderRequestEntity();
requestEntity.setBody(productDesc);
requestEntity.setOutTradeNo(generateRandomOrderNo());
requestEntity.setTotalFee(Utility.Yuan2Fen(Double.parseDouble(price)).toString());
requestEntity.setSpbillCreateIp(terminalIP);
requestEntity.setOpenid(openid);
requestEntity.setTradeType(tradeType);
String nonceStr = WXPayUtil.generateNonceStr();
requestEntity.setNonceStr(nonceStr);
requestEntity.setNotifyUrl(wcpConfigParams.getNotifyUrl());
Wxpay.fillrequestdata (data);
Map<String, String> respMap = wxPayDefault.unifiedOrder(beanToMap(requestEntity));
// The unified single interface was successfully invoked
if (respMap.get("return_code").equals(WXPayConstants.SUCCESS)
&& respMap.get("result_code").equals((WXPayConstants.SUCCESS))) {
String prepayId = respMap.get("prepay_id");
return wppSignatureUtil.permissionValidate(wcpConfigParams.getAppId(), nonceStr, requestUrl, prepayId,
wcpConfigParams.getApiKey(),wppBackendUtil.getJsApiTicket(wppBackendUtil.getAccessToken()));
} else if(! respMap.get("return_code").equals(WXPayConstants.SUCCESS)) {
Map<String, Object> map = new HashMap<>();
for (String key : respMap.keySet()) {
map.put(key, respMap.get(key));
}
returnmap; }}catch (Exception e) {
// log ...
}
return null; // Returns a map with an error message
}
/** * Universal wechat payment call method, flexible parameters *@paramRequestEntity UnifiedOrderRequestEntity unified order entity class *@paramRequestUrl Specifies the url of the request source *@return* /
public Map<String, Object> unifiedorder(UnifiedOrderRequestEntity requestEntity, String requestUrl) {
try {
String nonceStr = requestEntity.getNonceStr();
Map<String, String> respMap = wxPayDefault.unifiedOrder(beanToMap(requestEntity));
// The unified single interface was successfully invoked
if (respMap.get("return_code").equals(WXPayConstants.SUCCESS)
&& respMap.get("result_code").equals((WXPayConstants.SUCCESS))) {
String prepayId = respMap.get("prepay_id");
return wppSignatureUtil.permissionValidate(wcpConfigParams.getAppId(), nonceStr, requestUrl, prepayId,
wcpConfigParams.getApiKey(),wppBackendUtil.getJsApiTicket(wppBackendUtil.getAccessToken()));
} else if(! respMap.get("return_code").equals(WXPayConstants.SUCCESS)) {
Map<String, Object> map = new HashMap<>();
for (String key : respMap.keySet()) {
map.put(key, respMap.get(key));
}
returnmap; }}catch (Exception e) {
// log ...
}
return null; }}Copy the code
3. Method test
@SpringBootTest
class WppApplicationTests {
@Autowired
private WCPConfigParams wcpConfigParams;
@Autowired
private WCPBackendUtil wcpBackendUtil;
@Test
void testUnifiedOrder(a) {
String openid = "o4036jqo2PN9isV6N2FHGRsGRVqg"; // OpenID under ** public id
String ipAddr = "127.0.0.1";
String url = "http://chety.mynatapp.cc";
Map<String, Object> result1 = wcpBackendUtil.unifiedorder(openid, WCPBackendConst.TradeType.JSAPI.toString(), "1"."Test", ipAddr, url);
UnifiedOrderRequestEntity requestEntity = new UnifiedOrderRequestEntity();
requestEntity.setOutTradeNo(wcpBackendUtil.generateRandomOrderNo());
requestEntity.setBody("Test");
requestEntity.setOpenid("o4036jqo2PN9isV6N2FHGRsGRVqg");
requestEntity.setSpbillCreateIp(ipAddr);
requestEntity.setTradeType(WCPBackendConst.TradeType.JSAPI.toString());
requestEntity.setTotalFee("1");
requestEntity.setNotifyUrl("1"); requestEntity.setNonceStr(WXPayUtil.generateNonceStr()); requestEntity.setNotifyUrl(wcpConfigParams.getNotifyUrl()); Map<String, Object> result2 = wcpBackendUtil.unifiedorder(requestEntity,url); System.out.println(result1); System.out.println(result2); }}Copy the code
4. Return the result, as shown in the figure
emmm… There is something wrong with the public account. A normal return would look something like this:
{
"configMap": {
"appId": "wxa02348cd5ec17d28"."nonceStr": "K2pJsNrRIlhQbeCAVDrPLFTrRo0q1zNS"."signature": "f62e3c2a0e89973e548b046e8dd2d45f787d8b09"."timestamp": "1568187468"
},
"payMap": {
"timeStamp": "1568187468"."package": "prepay_id=wx11153748107050b2c5f8d4df1082400300"."packageStr": "prepay_id=wx11153748107050b2c5f8d4df1082400300"."paySign": "C7F135081A4476F434C67686403D741D"."appId": "wxa02348cd5ec17d28"."signType": "MD5"."nonceStr": "K2pJsNrRIlhQbeCAVDrPLFTrRo0q1zNS"}}Copy the code
Similarly, we will implement a query order function
Query order
1. Create an entity class for querying orders
@Data
public class OrderQueryRequestEntity {
/** * public ID */
private String appid;
/** * Merchant number */
@JSONField(name = "mch_id")
private String mchId;
/** * wechat order number */
@JSONField(name = "transaction_id")
private String transactionId;
// ...
}
Copy the code
2. Create a method for querying orders
/** **@paramOutTradeNo Merchant Order Number *@return* /
public Map<String, String> orderquery(String outTradeNo) {
try {
OrderQueryRequestEntity requestEntity = new OrderQueryRequestEntity();
requestEntity.setOutTradeNo(outTradeNo);
requestEntity.setNonceStr(WXPayUtil.generateNonceStr());
Map<String, String> map = orderquery(requestEntity);
return map;
} catch (Exception e) {
// log ...
}
return null;
}
/** **@paramRequestEntity OrderQueryRequestEntity requestEntity for order query *@return* /
public Map<String, String> orderquery(OrderQueryRequestEntity requestEntity) {
try {
return wxPayDefault.orderQuery(beanToMap(requestEntity));
} catch (Exception e) {
// log ...
}
return null;
}
Copy the code
3. Test the query order
@Test
void testQuery(a) {
Map<String, String> result1 = wcpBackendUtil.orderquery("201907051128063699"); // This order can be the merchant order number generated when placing a unified order
OrderQueryRequestEntity requestEntity = new OrderQueryRequestEntity();
requestEntity.setOutTradeNo("201907051128063699");
requestEntity.setNonceStr(WXPayUtil.generateNonceStr());
Map<String, String> result2 = wcpBackendUtil.orderquery(requestEntity);
System.out.println(result1);
System.out.println(result2);
}
Copy the code
4. Query result
The end of the
In this way, other methods of wechat payment can be similarly expanded, such as closing closeOrder, refundrefund (certificate required), refundquery refundQuery, download statement downloadbill, Pull order evaluation data batchqueryComment and so on are similar call processes.
At present, the refactored code has been exposed to coupling as much as possible to improve reusability, and has been tested. However, there are still many areas that need to be improved, such as wechat payment business joining merchant transaction business, token storage in Redis, front-end page improvement, etc., which will be updated continuously in the future, welcome to comment and leave modification suggestions.
The source code has been uploaded: github.com/chetwhy/wpp