生活随笔
收集整理的這篇文章主要介紹了
微信扫码支付实现
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
前言:在一次項(xiàng)目開(kāi)發(fā)中我的業(yè)務(wù)模塊涉及微信掃碼支付和支付寶掃碼支付。在查閱現(xiàn)有文檔后發(fā)現(xiàn),大部分文章還停留在V2支付,部分文章介紹了V3支付但是并不能走通,于是我便閱讀了官方文檔,形成了這篇總結(jié)性文章,希望對(duì)讀者有所幫助。
官方開(kāi)發(fā)文檔
官方SDK開(kāi)發(fā)工具包
API字典
文章目錄
- 微信平臺(tái)信息配置
- 獲取商戶(hù)號(hào)
- 獲取證書(shū)序列號(hào)
- 獲取API私鑰
- 獲取APIv3密鑰
- 獲取并綁定APPID
- 開(kāi)通支付產(chǎn)品
- 代碼中的配置
- 引入依賴(lài)
- 配置微信工具類(lèi)
- 支付請(qǐng)求
- 回調(diào)接口
微信平臺(tái)信息配置
相信此刻你一定已經(jīng)注冊(cè)并登陸過(guò) 微信開(kāi)放平臺(tái) 了,在此不多贅述。需在平臺(tái)中獲取的開(kāi)發(fā)所需信息如下:
MERCHANT_ID:商戶(hù)號(hào)
MERCHANT_SERIAL_NUMBER:商戶(hù)API證書(shū)的證書(shū)序列號(hào)
MERCHANT_PRIVATEKEY:商戶(hù)API私鑰
AIV3KEY:API v3密鑰
APP_ID:小程序的APPID
注意:想要獲取以上信息,必須是商戶(hù)主賬號(hào)才可以(即便是管理員,部分操作也無(wú)法完成)。
下面我們來(lái)逐個(gè)獲取以上參數(shù)
獲取商戶(hù)號(hào)
登錄微信開(kāi)放平臺(tái)如果是主賬號(hào),如圖紅框位置即為商戶(hù)號(hào)。如果不是商戶(hù)主帳號(hào),顯示的則為個(gè)人帳號(hào)(如圖為子管理員賬號(hào))如果登陸賬號(hào)不是主賬號(hào),則需要依次點(diǎn)擊產(chǎn)品中心→AppID賬號(hào)管理→+關(guān)聯(lián)AppID,此時(shí)即可看到商戶(hù)號(hào)
獲取證書(shū)序列號(hào)
該步驟操作需要商戶(hù)主賬號(hào)才可完成。
依次點(diǎn)擊賬戶(hù)中心→API安全,此時(shí)就進(jìn)入了API安全頁(yè),此頁(yè)面可設(shè)置一個(gè)證書(shū)兩個(gè)密鑰,API證書(shū)是根據(jù)網(wǎng)站提供的工具生成的(具體下載操作可參考網(wǎng)站中給出的指引)對(duì)微信工具生成的文件進(jìn)行解壓縮得到如下信息,后續(xù)操作可根據(jù)’證書(shū)使用說(shuō)明.txt進(jìn)行(提示一下:Windows用戶(hù)直接雙擊apiclient_cert.p12文件即可上傳證書(shū))。在根據(jù)網(wǎng)站指引完成了API證書(shū)上傳后,商戶(hù)主賬號(hào)就可直接點(diǎn)擊管理證書(shū)查看證書(shū)序列號(hào)。
獲取API私鑰
在獲取證書(shū)序列號(hào)的第二步操作中的apiclient_key.pem文件即為所需的商戶(hù)API私鑰。
注意:此處有一個(gè)小坑,在粘貼復(fù)制密鑰時(shí),除了保留-----BEGIN PRIVATE KEY-----和-----END PRIVATE KEY-----的換行外,中間內(nèi)容在粘貼時(shí)一定要去除復(fù)制時(shí)產(chǎn)生的換行符(推薦復(fù)制內(nèi)容后去網(wǎng)上的去換行工具中去除下?lián)Q行符)
獲取APIv3密鑰
依舊是在API安全頁(yè),直接點(diǎn)擊設(shè)置來(lái)設(shè)置APIv3密鑰即可。
APIv3密鑰要求是32個(gè)字符,支持?jǐn)?shù)字/大小寫(xiě)字母,可以在隨機(jī)生成字符串的線上工具中進(jìn)行生成將上一步生成的密鑰復(fù)制粘貼,完成手機(jī)號(hào)驗(yàn)證碼安全認(rèn)證即可完成APIv3密鑰的設(shè)置
獲取并綁定APPID
公眾號(hào)和小程序的AppID獲取方式雷同,具體參考如圖所示指引
登錄微信公眾平臺(tái),依次點(diǎn)擊設(shè)置→基本設(shè)置,頁(yè)面最下方賬號(hào)信息處即可看到AppID(小程序ID)
在微信公眾平臺(tái),依次點(diǎn)擊設(shè)置→基本設(shè)置,找到主體信息
回到微信開(kāi)放平臺(tái),依次點(diǎn)擊產(chǎn)品中心→AppID賬號(hào)管理→+關(guān)聯(lián)AppID,將上一步獲取到的AppID和主體信息粘貼在此并提交
回到微信公眾平臺(tái)授權(quán)即可
開(kāi)通支付產(chǎn)品
登錄微信開(kāi)放平臺(tái),來(lái)到產(chǎn)品中心頁(yè)面。開(kāi)啟支付產(chǎn)品中的Native支付(掃碼支付)功能。開(kāi)啟運(yùn)營(yíng)工具中的企業(yè)付款到零錢(qián)(付款資金直接進(jìn)入微信零錢(qián),提現(xiàn)方式根據(jù)用戶(hù)需求來(lái)開(kāi)通不同產(chǎn)品即可)。
代碼中的配置
對(duì)于v3支付來(lái)說(shuō),此時(shí)可謂萬(wàn)事俱備只欠東風(fēng),下面來(lái)配置代碼中的工具類(lèi)和第三方請(qǐng)求。
引入依賴(lài)
此處引入的是v3支付
<dependency><groupId>com.github.wechatpay-apiv3
</groupId><artifactId>wechatpay-apache-httpclient
</artifactId><version>0.3.0
</version>
</dependency>
配置微信工具類(lèi)
此工具類(lèi)僅供參考,配置信息也可根據(jù)個(gè)人喜好放在yml文件中(為方便閱讀此處放在類(lèi)中作為常量)。
此處的工具類(lèi)有以下作用:
配置商戶(hù)信息微信接口請(qǐng)求所需工具方法微信驗(yàn)簽
public class WeChartUtil {public static final String MERCHANT_ID
= "商戶(hù)id";public static final String MERCHANT_SERIAL_NUMBER
= "證書(shū)序列號(hào)";public static final String MERCHANT_PRIVATEKEY
= "-----BEGIN PRIVATE KEY-----\n" +
"你的密鑰內(nèi)容(記得去換行)" +"\n" + "-----END PRIVATE KEY-----";public static final String AIV3KEY
= "APIv3密鑰";public static final String NOTIFY_URL
= "回調(diào)地址";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();}}
}
支付請(qǐng)求
- 該請(qǐng)求方式參照文章開(kāi)頭提供的SDK開(kāi)發(fā)工具包中的請(qǐng)求方式
- 不同的產(chǎn)品應(yīng)參考 API字典 請(qǐng)求不同的地址,此處以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();}
回調(diào)接口
- 這里就是v3支付和v2支付一個(gè)比較明顯的區(qū)別點(diǎn),v2支付的回調(diào)地址是在官方網(wǎng)站中配置的,而v3支付中,回調(diào)地址是作為請(qǐng)求參數(shù)傳遞到微信的。
- 此接口地址對(duì)應(yīng)支付請(qǐng)求時(shí)的請(qǐng)求參數(shù)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
;}
總結(jié)
以上是生活随笔為你收集整理的微信扫码支付实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。