生活随笔
收集整理的這篇文章主要介紹了
微信扫码支付实现
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
前言:在一次項目開發中我的業務模塊涉及微信掃碼支付和支付寶掃碼支付。在查閱現有文檔后發現,大部分文章還停留在V2支付,部分文章介紹了V3支付但是并不能走通,于是我便閱讀了官方文檔,形成了這篇總結性文章,希望對讀者有所幫助。
官方開發文檔
官方SDK開發工具包
API字典
文章目錄
- 微信平臺信息配置
- 獲取商戶號
- 獲取證書序列號
- 獲取API私鑰
- 獲取APIv3密鑰
- 獲取并綁定APPID
- 開通支付產品
- 代碼中的配置
微信平臺信息配置
相信此刻你一定已經注冊并登陸過 微信開放平臺 了,在此不多贅述。需在平臺中獲取的開發所需信息如下:
MERCHANT_ID:商戶號
MERCHANT_SERIAL_NUMBER:商戶API證書的證書序列號
MERCHANT_PRIVATEKEY:商戶API私鑰
AIV3KEY:API v3密鑰
APP_ID:小程序的APPID
注意:想要獲取以上信息,必須是商戶主賬號才可以(即便是管理員,部分操作也無法完成)。
下面我們來逐個獲取以上參數
獲取商戶號
登錄微信開放平臺如果是主賬號,如圖紅框位置即為商戶號。如果不是商戶主帳號,顯示的則為個人帳號(如圖為子管理員賬號)如果登陸賬號不是主賬號,則需要依次點擊產品中心→AppID賬號管理→+關聯AppID,此時即可看到商戶號
獲取證書序列號
該步驟操作需要商戶主賬號才可完成。
依次點擊賬戶中心→API安全,此時就進入了API安全頁,此頁面可設置一個證書兩個密鑰,API證書是根據網站提供的工具生成的(具體下載操作可參考網站中給出的指引)對微信工具生成的文件進行解壓縮得到如下信息,后續操作可根據’證書使用說明.txt進行(提示一下:Windows用戶直接雙擊apiclient_cert.p12文件即可上傳證書)。在根據網站指引完成了API證書上傳后,商戶主賬號就可直接點擊管理證書查看證書序列號。
獲取API私鑰
在獲取證書序列號的第二步操作中的apiclient_key.pem文件即為所需的商戶API私鑰。
注意:此處有一個小坑,在粘貼復制密鑰時,除了保留-----BEGIN PRIVATE KEY-----和-----END PRIVATE KEY-----的換行外,中間內容在粘貼時一定要去除復制時產生的換行符(推薦復制內容后去網上的去換行工具中去除下換行符)
獲取APIv3密鑰
依舊是在API安全頁,直接點擊設置來設置APIv3密鑰即可。
APIv3密鑰要求是32個字符,支持數字/大小寫字母,可以在隨機生成字符串的線上工具中進行生成將上一步生成的密鑰復制粘貼,完成手機號驗證碼安全認證即可完成APIv3密鑰的設置
獲取并綁定APPID
公眾號和小程序的AppID獲取方式雷同,具體參考如圖所示指引
登錄微信公眾平臺,依次點擊設置→基本設置,頁面最下方賬號信息處即可看到AppID(小程序ID)
在微信公眾平臺,依次點擊設置→基本設置,找到主體信息
回到微信開放平臺,依次點擊產品中心→AppID賬號管理→+關聯AppID,將上一步獲取到的AppID和主體信息粘貼在此并提交
回到微信公眾平臺授權即可
開通支付產品
登錄微信開放平臺,來到產品中心頁面。開啟支付產品中的Native支付(掃碼支付)功能。開啟運營工具中的企業付款到零錢(付款資金直接進入微信零錢,提現方式根據用戶需求來開通不同產品即可)。
代碼中的配置
對于v3支付來說,此時可謂萬事俱備只欠東風,下面來配置代碼中的工具類和第三方請求。
引入依賴
此處引入的是v3支付
<dependency><groupId>com.github.wechatpay-apiv3
</groupId><artifactId>wechatpay-apache-httpclient
</artifactId><version>0.3.0
</version>
</dependency>
配置微信工具類
此工具類僅供參考,配置信息也可根據個人喜好放在yml文件中(為方便閱讀此處放在類中作為常量)。
此處的工具類有以下作用:
配置商戶信息微信接口請求所需工具方法微信驗簽
public class WeChartUtil {public static final String MERCHANT_ID
= "商戶id";public static final String MERCHANT_SERIAL_NUMBER
= "證書序列號";public static final String MERCHANT_PRIVATEKEY
= "-----BEGIN PRIVATE KEY-----\n" +
"你的密鑰內容(記得去換行)" +"\n" + "-----END PRIVATE KEY-----";public static final String AIV3KEY
= "APIv3密鑰";public static final String NOTIFY_URL
= "回調地址";public static final String APP_ID
= "小程序的AppID";public static String orderNo(){return UUID
.randomUUID().toString().replaceAll("-", "").substring(0, 32);}public static String timeExpire(){long time
= 5*60*1000;SimpleDateFormat simpleDateFormat
= new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");Date now
= new Date();Date afterDate
= new Date(now
.getTime() + time
);return simpleDateFormat
.format(afterDate
);}public static BigDecimal transition(Integer num
) {BigDecimal bigDecimal1
= new BigDecimal(num
+ "");BigDecimal bigDecimal2
= new BigDecimal("100.00");return bigDecimal1
.divide(bigDecimal2
).setScale(2);}public static boolean signVerify(String serial
, String message
, String signature
) {try {PrivateKey key
= PemUtil.loadPrivateKey(new ByteArrayInputStream(MERCHANT_PRIVATEKEY
.getBytes(StandardCharsets.UTF_8
)));ScheduledUpdateCertificatesVerifier verifier
= new ScheduledUpdateCertificatesVerifier(new WechatPay2Credentials(MERCHANT_ID
, new PrivateKeySigner(MERCHANT_SERIAL_NUMBER
, key
)),AIV3KEY
.getBytes(StandardCharsets.UTF_8
));return verifier
.verify(serial
, message
.getBytes(StandardCharsets.UTF_8
), signature
);} catch (Exception e
) {e
.printStackTrace();}return false;}public static String decryptOrder(String body
){try {AesUtil util
= new AesUtil(AIV3KEY
.getBytes(StandardCharsets.UTF_8
));ObjectMapper objectMapper
= new ObjectMapper();JsonNode node
= objectMapper
.readTree(body
);JsonNode resource
= node
.get("resource");String ciphertext
= resource
.get("ciphertext").textValue();String associatedData
= resource
.get("associated_data").textValue();String nonce
= resource
.get("nonce").textValue();return util
.decryptToString(associatedData
.getBytes(StandardCharsets.UTF_8
), nonce
.getBytes(StandardCharsets.UTF_8
), ciphertext
);} catch (IOException | GeneralSecurityException e
) {e
.printStackTrace();}return null;}public static void closeOrder(String outTradeNo
) {PrivateKey key
= PemUtil.loadPrivateKey(new ByteArrayInputStream(MERCHANT_PRIVATEKEY
.getBytes(StandardCharsets.UTF_8
)));ScheduledUpdateCertificatesVerifier verifier
= new ScheduledUpdateCertificatesVerifier(new WechatPay2Credentials(MERCHANT_ID
, new PrivateKeySigner(MERCHANT_SERIAL_NUMBER
, key
)),AIV3KEY
.getBytes(StandardCharsets.UTF_8
));WechatPayHttpClientBuilder builder
= WechatPayHttpClientBuilder.create().withMerchant(MERCHANT_ID
, MERCHANT_SERIAL_NUMBER
, key
).withValidator(new WechatPay2Validator(verifier
));CloseableHttpClient httpClient
= builder
.build();HttpPost httpPost
= new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/"+outTradeNo
+"/close");httpPost
.addHeader("Accept", "application/json");httpPost
.addHeader("Content-type","application/json; charset=utf-8");try {ByteArrayOutputStream bos
= new ByteArrayOutputStream();ObjectMapper objectMapper
= new ObjectMapper();ObjectNode rootNode
= objectMapper
.createObjectNode();rootNode
.put("mchid",MERCHANT_ID
);objectMapper
.writeValue(bos
, rootNode
);httpPost
.setEntity(new StringEntity(bos
.toString("UTF-8"), "UTF-8"));CloseableHttpResponse response
= httpClient
.execute(httpPost
);String bodyAsString
= EntityUtils.toString(response
.getEntity());System.out
.println(bodyAsString
);} catch (IOException e
) {e
.printStackTrace();}}
}
支付請求
- 該請求方式參照文章開頭提供的SDK開發工具包中的請求方式
- 不同的產品應參考 API字典 請求不同的地址,此處以Native支付為例
PrivateKey key
= PemUtil.loadPrivateKey(new ByteArrayInputStream(WeChartUtil.MERCHANT_PRIVATEKEY
.getBytes(StandardCharsets.UTF_8
)));ScheduledUpdateCertificatesVerifier verifier
= new ScheduledUpdateCertificatesVerifier(new WechatPay2Credentials(WeChartUtil.MERCHANT_ID
, new PrivateKeySigner(WeChartUtil.MERCHANT_SERIAL_NUMBER
, key
)),WeChartUtil.AIV3KEY
.getBytes(StandardCharsets.UTF_8
));WechatPayHttpClientBuilder builder
= WechatPayHttpClientBuilder.create().withMerchant(WeChartUtil.MERCHANT_ID
, WeChartUtil.MERCHANT_SERIAL_NUMBER
, key
).withValidator(new WechatPay2Validator(verifier
));CloseableHttpClient httpClient
= builder
.build();HttpPost httpPost
= new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/native");httpPost
.addHeader("Accept", "application/json");httpPost
.addHeader("Content-type","application/json; charset=utf-8");ByteArrayOutputStream bos
= new ByteArrayOutputStream();ObjectMapper objectMapper
= new ObjectMapper();String outTradeNo
= WeChartUtil.orderNo();ObjectNode rootNode
= objectMapper
.createObjectNode();rootNode
.put("mchid",WeChartUtil.MERCHANT_ID
).put("appid", WeChartUtil.APP_ID
).put("notify_url", WeChartUtil.NOTIFY_URL
).put("out_trade_no", outTradeNo
).put("time_expire", WeChartUtil.timeExpire()).put("description", description
); rootNode
.putObject("amount").put("total", 1); try {objectMapper
.writeValue(bos
, rootNode
);httpPost
.setEntity(new StringEntity(bos
.toString("UTF-8"), "UTF-8"));CloseableHttpResponse response
= httpClient
.execute(httpPost
);String bodyAsString
= EntityUtils.toString(response
.getEntity());JSONObject details
= JSONObject.parseObject(bodyAsString
);String qrCode
= details
.getString("code_url");} catch (IOException e
) {e
.printStackTrace();}
回調接口
- 這里就是v3支付和v2支付一個比較明顯的區別點,v2支付的回調地址是在官方網站中配置的,而v3支付中,回調地址是作為請求參數傳遞到微信的。
- 此接口地址對應支付請求時的請求參數notify_url,具體功能介紹在此不做闡述
public Map wxCallback(HttpServletRequest request
) {Map result
= new HashMap();result
.put("code", "FAIL");try {StringBuilder signStr
= new StringBuilder();signStr
.append(request
.getHeader("Wechatpay-Timestamp")).append("\n");signStr
.append(request
.getHeader("Wechatpay-Nonce")).append("\n");BufferedReader br
= request
.getReader();String str
= null;StringBuilder builder
= new StringBuilder();while ((str
= br
.readLine()) != null){builder
.append(str
);}signStr
.append(builder
.toString()).append("\n");if (!WeChartUtil.signVerify(request
.getHeader("Wechatpay-Serial"), signStr
.toString(), request
.getHeader("Wechatpay-Signature"))){result
.put("message", "sign error");return result
;}String decryptOrder
= WeChartUtil.decryptOrder(builder
.toString());JSONObject details
= JSONObject.parseObject(decryptOrder
);String ciphertext
= details
.getString("trade_state");if ("SUCCESS".equals(ciphertext
)){result
.put("code", "SUCCESS");result
.put("message", "成功");WeChartUtil.closeOrder(details
.getString("out_trade_no"));}} catch (IOException e
) {e
.printStackTrace();TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}return result
;}
總結
以上是生活随笔為你收集整理的微信扫码支付实现的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。