It needs a total of 5
1.pom, add the following dependent
<dependency> <groupId>com.github.wxpaygroupId> <artifactId>wxpay-sdkartifactId> <version>0.0.3version> dependency>
2. configuration file (folder):
WxpayConfig.properties
# Micro-channel payment arrangement # mobile end AppID APP_ID = wx3d3c125230d2ba ** # merchant number BUSINESS_CODE = 153 982 *** # keys --sign obtain required in Account Center -> api security --api keys set API_KEY = gin201907 * ********** APIV3_KEY = GIN2 ******* # signature type HMAC-SHA256 and MD5, the default is MD5 sign_type = MD5 # certificates address PEM_ADDRESS = /wxConfig/**/apiclient_cert.p12 # asynchronous notification address (Note that you must be outside the network) notify_url = HTTP: // *** / wxAppPay / the notify # number of public appID - to be determined --- #GZH_APP_ID = wxcfe45ec ****
While another folder in the same directory certificate, micro-channel pay part of the function requires a certificate
3. The sign of the generated class util: WxMD5Util
import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.Map; import java.util.Set; /** * Created by Mafy on 2019/7/9. * 该类是生成签名用 */ public class WxMD5Util { public static String getSign(Map data) throws Exception { WXConfigUtil config = WXConfigUtil.initWXconfigUtil(); Set keySet = data.keySet(); String[] keyArray = keySet.toArray(new String[keySet.size()]); Arrays.sort(keyArray); StringBuilder sb = new StringBuilder(); for (String k : keyArray) { if (k.equals(WXPayConstants.FIELD_SIGN)) { continue; } if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名 sb.append(k).append("=").append(data.get(k).trim()).append("&"); } sb.append("key=").append(config.getKey()); MessageDigest md = null; try { md = MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } byte[] array = new byte[0]; try { array = md.digest(sb.toString().getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { e.printStackTrace (); } StringBuilder sb2 = new StringBuilder(); for (byte item : array) { sb2.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3)); } return sb2.toString().toUpperCase(); } }
4. Get profile configuration class - there singleton pattern
import com.github.wxpay.sdk.WXPayConfig; import org.springframework.util.ObjectUtils; import java.io.*; import java.util.Properties; /** * Created by Mafy on 2019/7/9. */ public class WXConfigUtil implements WXPayConfig{ Properties properties= null; private byte[] certData; private static WXConfigUtil wxConfigUtil = new WXConfigUtil(); public static WXConfigUtil initWXconfigUtil(){ return wxConfigUtil; } private WXConfigUtil() { String certPath = PropertiesUtil.class.getResource(getProperties().getProperty("PEM_ADDRESS")).getPath();//从微信商户平台下载的安全证书存放的路径 InputStream certStream = null; try { File file = new File(certPath); certStream = new FileInputStream(file); this.certData = new byte[(int) file.length()]; certStream.read(this.certData); } catch (IOException e) { e.printStackTrace(); }finally { if(!ObjectUtils.isEmpty(certStream)){ try { certStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } @Override public String getAppID() { return getProperties().getProperty("APP_ID"); } //parnerid,商户号 @Override public String getMchID() { return getProperties().getProperty("BUSINESS_CODE"); } @Override public String getKey() { return getProperties().getProperty("API_KEY"); } @Override public InputStream getCertStream() { ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData); return certBis; } @Override public int getHttpConnectTimeoutMs() { return 8000; } @Override public int getHttpReadTimeoutMs() { return 10000; } Properties getProperties() { if(null == properties) { properties = PropertiesUtil.getProperties("/wxConfig/WxpayConfig.properties"); } return properties; } }
5. Call controller interface payment: WxAppPayController
import com.alibaba.fastjson.JSONObject; import com.github.wxpay.sdk.WXPay; import com.github.wxpay.sdk.WXPayUtil; import com.xinlianpu.util.PropertiesUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; java.io.InputStreamReader Import; Import java.util.HashMap; Import java.util.Map; Import java.util.Properties; / ** * the Created by MAFy ON 2019/7/9. * / @Controller @ResponseBody @ RequestMapping ( "/ wxAppPay") public class WxAppPayController { static the Properties Properties = null; Final static Logger Logger = LoggerFactory.getLogger (WxAppPayController.class); // @Resource // Private wxPayService wxPayService; // asynchronous notification address (note must be extranet) / ** * unified orders - no certificate public static final String NOTIFY_URL = getProperties().getProperty("NOTIFY_URL"); // transaction type, JSAPI - JSAPI pay (or applet payment), NATIVE - Native payment, APP - app payments, MWEB - H5 pay public static TRADE_TYPE_APP Final String = "APP"; * includes generating a pre-pay orders -> order generation -> return to the front-end order data * official document: HTTPS: //pay.weixin.qq.com/wiki/doc/api/app/app.php Chapter 9_1 =? * @param jsonObject * @ return * @throws Exception * / @ RequestMapping (value = "/ the Pay", Method, = RequestMethod.POST) // public the Map wxPay (@RequestParam (value = "userId") String user_id, @RequestParam (value = "totalFee") total_fee String) throws Exception { public DataResponse wxPayUnEncryption (@RequestBody the jSONObject jsonObject) throws Exception { logger.info ( "single payment, app pass over the parameter: {}", jsonObject); DataResponse = DataResponse.success DataResponse (); // String user_id = TokenManager.getUserId (); String user_id = "123"; // String total_fee = jsonObject.getString ( "totalFee"); // total value String total_fee = "1"; // total amount - points String spbillCreateIp = jsonObject.getString ( "spbillCreateIp "); // click on the button of the machine to pay ip // spbillCreateIp String =" 0.01 "; // click on the button of the machine to pay ip the Map the Map = null; the try { // the attach String =" {\ "user_id \": \ "" + user_id + "\ "} "; // extension field is returned as String attach =" extension field "; // extension field is returned as // request pre-payment orders Map result = dounifiedOrder (attach, total_fee , spbillCreateIp ); logger.info ( "before the next single, pre-paid return information: {}", JSONObject.toJSON (result)); whether it can be determined // change the order of data correctly prepaid after // pre-payment orders generated with the signature of the client your payment information map = new HashMap <> (); // returns APP-side data // participate in the signature tune from the field and have to pay only 6, respectively appid, partnerid, prepayid, package, noncestr and timestamp, and must be lowercase --- start-- map.put ( "AppID", result.get ( "AppID")); map.put ( "partnerid", result.get ( "mch_id")); map.put ( "prepayid", result.get ( "prepay_id ")); map.put (" Package "," Sign = WXPay "); map.put (" noncestr ", result.get (" nonce_str ")); String signTimstamp = String.valueOf (System.currentTimeMillis () / 1000); map.put ( "timestamp", signTimstamp); // seconds // participate transfer payments from the signature field and there are only six, respectively appid, partnerid, prepayid, package, noncestr and timestamp, and requires lowercase end-- --- the Map = new new mobileParam the HashMap <> ();mch_id")); mobileParam.put("prepayId", result.get("prepay_id")); mobileParam.put ( "for appId", result.get ( "AppID")); mobileParam.put ( "partnerId", result.get ( "mch_id")); mobileParam.put ( "packageValue", "Sign = WXPay"); //? ? ? mobileParam.put ( "nonceStr", result.get ( "nonce_str")); mobileParam.put ( "timeStamp", signTimstamp); // seconds to return here when // ---- Do not use request pre-payment orders signature ------- mobileParam.put ( "Sign", WxMD5Util.getSign (Map)); mobileParam.put ( "extData", the attach); logger.info ( "pay return parameter: {}", JSONObject. the toJSONString (Map)); logger.info ( "the return movement end parameter: {}", JSONObject.toJSONString (mobileParam)); dataResponse.setData (mobileParam); } the catch (Exception E) { return DataResponse.failure (); } Return dataResponse; } / ** * Micro-channel interfaces Refund - refund * @param jsonObject * @return * / public DataResponse wxRefund (@RequestBody the JSONObject jsonObject) { DataResponse Model DataResponse.success = (); String = jsonObject.getString the orderId ( "the orderId"); // order ID the try { WXConfigUtil WXConfigUtil.initWXconfigUtil config = (); wXPay wxpay = new new wXPay (config); the Map Data new new = the HashMap <> (); the Map = Refund wxpay.refund (Data); } the catch (Exception E) { e.printStackTrace(); } Return Model; } / ** * payment asynchronous result notification, we passed when requesting pre-payment orders address - no parameters * official document: https: //pay.weixin.qq.com/wiki/doc/api /app/app.php?chapter=9_7&index=3 * / @RequestMapping (value = "/ Notify", Method RequestMethod.GET = {,} RequestMethod.POST) public String wxPayNotifyUnEncryption (the HttpServletRequest Request, the HttpServletResponse Response) { String = resXml ""; the try { InputStream request.getInputStream inputStream = (); // converted into the InputStream xmlString to the BufferedReader Reader the BufferedReader new new = (the InputStreamReader new new (inputStream)); the StringBuilder the StringBuilder new new SB = (); Line = null String; the try { while ((line = reader.readLine()) != null) { sb.append(line + "\n"); } } the catch (IOException E) { System.out.println (e.getMessage ()); } {the finally the try { inputStream.close (); } the catch (IOException E) { e.printStackTrace ( ); } } resXml sb.toString = (); String Result = PAYBACK (resXml); return Result; } the catch (Exception E) { logger.info ( "micro-channel mobile payment failed:" + e.getMessage ()); the System .out.println ( "micro-channel mobile payment failure:" + e.getMessage ()); The Result = String "" + "" + "" + ""; return the Result; } } / ** * official SDK calls to obtain pre-payment orders and other parameters * * @param The attach additional parameters * @param total_fee total price * @return * Exception @throws * / public dounifiedOrder the Map (the attach String, String total_fee, String spbillCreateIp) throws Exception { logger.info ( "call into sdk official method parameters: {}, {}", the attach, total_fee); the Map returnMap is new new = the HashMap <> (); WXConfigUtil WXConfigUtil.initWXconfigUtil config = (); WXPay wxpay = new new WXPay (config); the Map Data new new = the HashMap <> (); // generate business order number, can not be repeated String out_trade_no = "wxpay "+ System.currentTimeMillis (); data.put (" AppID ", config.getAppID ()); // application ID data.put (" mch_id ", config.getMchID ()); // number of merchants data.put ( "nonce_str", WXPayUtil.generateNonceStr ( )); // random string not longer than 32 bits. Recommended random number generation algorithm String body = "New Face micro-channel payment orders - Test -" + System.currentTimeMillis (); data.put ( "body", body); // Product Description data.put ( "out_trade_no", out_trade_no) ; // merchant Order number data.put ( "total_fee", total_fee) ; // total amount data.put ( "spbill_create_ip", spbillCreateIp) ; // machine IP address of your server --- pay for a click IP data.put ( "notify_url", nOTIFY_URL); // asynchronous notification address (Note that you must be outside the network) data.put ( "trade_type", TRADE_TYPE_APP); // transaction type data.put ( "attach", attach) ; // additional data, as they are returned in the query API and payment notification, this field is mainly used to customize the data carried by the merchant orders String sign1 = WxMD5Util.getSign (the data); data.put ( "sign", sign1) ; // signature returnMap.put (" AppID ",response.get("appid")); logger.info ( "single reservation generated parameters, {}", JSONObject.toJSON (Data)); the try { // use the official API requests prepaid orders the Map wxpay.unifiedOrder Response = (Data); logger.info ( "preorder returns the result, {} ", JSONObject.toJSON (Response)); String response.get the returnCode = (" the return_code "); // Get return code // if the return code is SUCCESS, it returns a result_code, then the result_code is judged if (returnCode.equals ( "SUCCESS") ) {// return the following five main parameters logger.info ( "return result preorder SUCCESS, {}", JSONObject.toJSON (Response)); String = the resultCode response.get ( "RESULT_CODE"); returnMap.put("nonce_str", response.get("nonce_str")); returnMap.put ( "mch_id", response.get ( "mch_id")); } else { returnMap.put ( "Sign", response.get ( "Sign")); IF ( "SUCCESS" .equals (resultCode)) {// resultCode to SUCCESS, and will not return prepay_id trade_type // get pre-payment transaction answer logo returnMap.put ( "trade_type", response.get ( "trade_type")); returnMap.put ( "prepay_id", response.get ( "prepay_id")); return returnMap is; } the else { logger.info ( "single reservation return results ERROR, {} ", JSONObject.toJSON (Response)); // This will return data is not paid orders return returnMap is; } return returnMap is; } } catch (Exception e) { System.out.println (E); other errors when the system // } return returnMap is; } / ** * @param notifyData the XML data asynchronous notification * @return * / public String PAYBACK (String notifyData) { logger.info ( "asynchronous notification data entry method: {}", notifyData); WXConfigUtil config = null; the try { config = WXConfigUtil.initWXconfigUtil (); } the catch (Exception E) { e.printStackTrace (); } wXPay wxpay new new WXPay = (config); String xmlBack = ""; the Map notifyMap = null; the try { notifyMap = WXPayUtil.xmlToMap (notifyData); // call official SDK map type data is converted into logger.info ( "Asynchronous payment result notification: {}", JSONObject.toJSONString (notifyMap)); IF (wxpay.isPayResultNotifySignatureValid (notifyMap )) {// verify the signature is valid, then the effective further processing String return_code = notifyMap.get ( "return_code" ); // state String out_trade_no = notifyMap.get ( "out_trade_no" ); // merchant order number if (return_code. the equals ( "SUCCESS")) { IF (out_trade_no!Null =) { // Special Note: The order has been refunded, but received a notification of a successful result of payment, order status should not be changed to a refund from the merchant's payment success // special situation: the same micro-channel notification service end may be repeatedly sent to the merchant system, it is necessary to check whether the data has been processed before persistence, treatment success sign a direct return // business data persistence System.err.println ( "Payment Successful"); logger.info ( "micro-channel mobile payment successful callback order number: {}", out_trade_no); xmlBack = "" + "" + "" + ""; } the else { Logger .info ( "micro-channel mobile payment failed callback order number: {}", out_trade_no); xmlBack = "" + "" + "" + ""; } } return xmlBack; } the else { // error signature, if there are no data sign field, also considered a signature error // Do not store the data fail to be? logger.error ( "mobile payment notification callback signature error"); xmlBack = "" } catch (Exception e) { logger.error("手机支付回调通知失败", e); xmlBack = "" + "" + "" + " "; } return xmlBack; } static Properties getProperties() { if(null == properties) { properties = PropertiesUtil.getProperties("/wxConfig/WxpayConfig.properties"); } return properties; }