為了在保證支付安全的前提下,帶給商戶簡單、一致且易用的開發(fā)體驗,我們推出了全新的微信支付APIv3接口。該版本API的具體規(guī)則請參考“APIv3接口規(guī)則”
備注:當(dāng)前接口用于微信國內(nèi)錢包
為了幫助開發(fā)者調(diào)用開放接口,我們提供了JAVA、PHP、GO三種語言版本的開發(fā)庫,封裝了簽名生成、簽名驗證、敏感信息加/解密、媒體文件上傳等基礎(chǔ)功能(更多語言版本的開發(fā)庫將在近期陸續(xù)提供)
測試步驟:
1、根據(jù)自身開發(fā)語言,選擇對應(yīng)的開發(fā)庫并構(gòu)建項目,具體配置請參考下面鏈接的詳細(xì)說明:
? wechatpay-java(推薦)wechatpay-apache-httpclient,適用于Java開發(fā)者。
? wechatpay-php(推薦)、wechatpay-guzzle-middleware,適用于PHP開發(fā)者
注:當(dāng)前開發(fā)指引接口PHP示例代碼采用wechatpay-guzzle-middleware版本
? wechatpay-go,適用于Go開發(fā)者
更多資源可前往微信支付開發(fā)者社區(qū)搜索查看
2、創(chuàng)建加載商戶私鑰、加載平臺證書、初始化httpClient的通用方法
@Before
public void setup() throws IOException {
// 加載商戶私鑰(privateKey:私鑰字符串)
PrivateKey merchantPrivateKey = PemUtil
.loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes("utf-8")));
// 加載平臺證書(mchId:商戶號,mchSerialNo:商戶證書序列號,apiV3Key:V3密鑰)
AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
new WechatPay2Credentials(mchId, new PrivateKeySigner(mchSerialNo, merchantPrivateKey)),apiV3Key.getBytes("utf-8"));
// 初始化httpClient
httpClient = WechatPayHttpClientBuilder.create()
.withMerchant(mchId, mchSerialNo, merchantPrivateKey)
.withValidator(new WechatPay2Validator(verifier)).build();
}
@After
public void after() throws IOException {
httpClient.close();
}
3、基于接口的示例代碼,替換請求參數(shù)后可發(fā)起測試
說明:
? 上面的開發(fā)庫為微信支付官方開發(fā)庫,其它沒有審核或者控制下的第三方工具和庫,微信支付不保證它們的安全性和可靠性
通過包管理工具引入SDK后,可根據(jù)下面每個接口的示例代碼替換相關(guān)參數(shù)后進(jìn)行快速測試
? 開發(fā)者如果想詳細(xì)了解簽名生成、簽名驗證、敏感信息加/解密、媒體文件上傳等常用方法的具體代碼實現(xiàn),可閱讀下面的詳細(xì)說明:
1.簽名生成
2.簽名驗證
3.敏感信息加解密
? 如想更詳細(xì)的了解我們的接口規(guī)則,可查看我們的接口規(guī)則指引文檔
? 支付授權(quán)目錄說明:
a、商戶最后請求拉起微信支付收銀臺的頁面地址我們稱之為“支付目錄”,例如:https://www.weixin.com/pay.php。
b、商戶實際的支付目錄必須和在微信支付商戶平臺設(shè)置的一致,否則會報錯“當(dāng)前頁面的URL未注冊:”
? 支付授權(quán)目錄設(shè)置說明:
登錄微信支付【商戶平臺->產(chǎn)品中心->開發(fā)配置】,設(shè)置后一般5分鐘內(nèi)生效。
圖1 支付授權(quán)目錄設(shè)置說明
? 支付授權(quán)目錄校驗規(guī)則說明:
a、如果支付授權(quán)目錄設(shè)置為頂級域名(例如:https://www.weixin.com/ ),那么只校驗頂級域名,不校驗后綴;
b、如果支付授權(quán)目錄設(shè)置為多級目錄,就會進(jìn)行全匹配,例如設(shè)置支付授權(quán)目錄為https://www.weixin.com/abc/123/,則實際請求頁面目錄不能為https://www.weixin.com/abc/,也不能為https://www.weixin.com/abc/123/pay/,必須為https://www.weixin.com/abc/123/
圖2 支付授權(quán)目錄校驗規(guī)則說明
? 授權(quán)域名說明:開發(fā)JSAPI支付時,在JSAPI下單接口中要求必傳用戶openid,而獲取openid則需要您在公眾平臺設(shè)置獲取openid的域名,只有被設(shè)置過的域名才是一個有效的獲取openid的域名,否則將獲取失敗。具體配置頁如圖2所示
? 授權(quán)域名設(shè)置說明:登錄【微信公眾平臺->公眾號設(shè)置->功能設(shè)置】
圖2
微信網(wǎng)頁授權(quán)域名設(shè)置
重點步驟說明:
步驟3 用戶下單發(fā)起支付,商戶可通過JSAPI下單創(chuàng)建支付訂單。
步驟8 商戶可在微信瀏覽器內(nèi)通過JSAPI調(diào)起支付API調(diào)起微信支付,發(fā)起支付請求。
步驟15 用戶支付成功后,商戶可接收到微信支付支付結(jié)果通知支付結(jié)果通知API。
步驟20 商戶在沒有接收到微信支付結(jié)果通知的情況下需要主動調(diào)用查詢訂單API查詢支付結(jié)果。
文檔展示了如何使用微信支付服務(wù)端 SDK 快速接入JSAPI支付產(chǎn)品,完成與微信支付對接的部分。
注意:
步驟說明:用戶通過商戶下發(fā)的模板消息或掃描二維碼在微信內(nèi)進(jìn)入商戶網(wǎng)頁,當(dāng)用戶選擇相關(guān)商戶購買時,商戶系統(tǒng)先調(diào)用該接口在微信支付服務(wù)后臺生成預(yù)支付交易單。
示例代碼
public void CreateOrder() throws Exception{
//請求URL
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
// 請求body參數(shù)
String reqdata = "{"
+ "\"amount\": {"
+ "\"total\": 100,"
+ "\"currency\": \"CNY\""
+ "},"
+ "\"mchid\": \"1900006891\","
+ "\"description\": \"Image形象店-深圳騰大-QQ公仔\(zhòng)","
+ "\"notify_url\": \"https://www.weixin.qq.com/wxpay/pay.php\","
+ "\"payer\": {"
+ "\"openid\": \"o4GgauE1lgaPsLabrYvqhVg7O8yA\"" + "},"
+ "\"out_trade_no\": \"1217752501201407033233388881\","
+ "\"goods_tag\": \"WXG\","
+ "\"appid\": \"wxdace645e0bc2c424\"" + "}";
StringEntity entity = new StringEntity(reqdata,"utf-8");
entity.setContentType("application/json");
httpPost.setEntity(entity);
httpPost.setHeader("Accept", "application/json");
//完成簽名并執(zhí)行請求
CloseableHttpResponse response = httpClient.execute(httpPost);
try {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) {
System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
} else if (statusCode == 204) {
System.out.println("success");
} else {
System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
throw new IOException("request failed");
}
} finally {
response.close();
httpClient.close();
}
}
重要入?yún)⒄f明:
? out_trade_no:商戶系統(tǒng)內(nèi)部訂單號,只能是數(shù)字、大小寫字母_-*且在同一個商戶號下唯一
? description:商品描述
? notify_url:支付回調(diào)通知URL,該地址必須為直接可訪問的URL,不允許攜帶查詢串
? total:訂單總金額,單位為分
? openid:openid是微信用戶在appid下的唯一用戶標(biāo)識(appid不同,則獲取到的openid就不同),可用于永久標(biāo)記一個用戶。openid獲取方式請參考以下文檔小程序獲取openid、公眾號獲取openid、APP獲取openid
更多參數(shù)、響應(yīng)詳情及錯誤碼請參見JSAPI下單接口文檔
步驟說明:通過JSAPI下單API成功獲取預(yù)支付交易會話標(biāo)識(prepay_id)后,需要通過JSAPI調(diào)起支付API來調(diào)起微信支付收銀臺
注意:
? WeixinJSBridge內(nèi)置對象在其他瀏覽器中無效
? 此API需要將請求參數(shù)進(jìn)行簽名(參與簽名的參數(shù)為:appId、timeStamp、nonceStr、package,參數(shù)區(qū)分大小寫)
示例代碼:
function onBridgeReady() {
WeixinJSBridge.invoke('getBrandWCPayRequest', {
"appId": "wx2421b1c4370ecxxx", //公眾號ID,由商戶傳入
"timeStamp": "1395712654", //時間戳,自1970年以來的秒數(shù)
"nonceStr": "e61463f8efa94090b1f366cccfbbb444", //隨機串
"package": "prepay_id=wx21201855730335ac86f8c43d1889123400",
"signType": "RSA", //微信簽名方式:
"paySign": "oR9d8PuhnIc+YZ8cBHFCwfgpaK9gd7vaRvkYD7rthRAZ\/X+QBhcCYL21N7cHCTUxbQ+EAt6Uy+lwSN22f5YZvI45MLko8Pfso0jm46v5hqcVwrk6uddkGuT+Cdvu4WBqDzaDjnNa5UK3GfE1Wfl2gHxIIY5lLdUgWFts17D4WuolLLkiFZV+JSHMvH7eaLdT9N5GBovBwu5yYKUR7skR8Fu+LozcSqQixnlEZUfyE55feLOQTUYzLmR9pNtPbPsu6WVhbNHMS3Ss2+AehHvz+n64GDmXxbX++IOBvm2olHu3PsOUGRwhudhVf7UcGcunXt8cqNjKNqZLhLw4jq\/xDg==" //微信簽名
},
function(res) {
if (res.err_msg == "get_brand_wcpay_request:ok") {
// 使用以上方式判斷前端返回,微信團(tuán)隊鄭重提示:
//res.err_msg將在用戶支付成功后返回ok,但并不保證它絕對可靠。
}
});
}
if (typeof WeixinJSBridge == "undefined") {
if (document.addEventListener) {
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
} else if (document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
} else {
onBridgeReady();
}
重要入?yún)⒄f明:
? package:JSAPI下單接口返回的prepay_id參數(shù)值,提交格式如:prepay_id=***
? signType:該接口V3版本僅支持RSA
? paySign:簽名
paySign生成規(guī)則、響應(yīng)詳情及錯誤碼請參見 JSAPI調(diào)起支付接口文檔
步驟說明:當(dāng)用戶完成支付,微信會把相關(guān)支付結(jié)果將通過異步回調(diào)的方式通知商戶,商戶需要接收處理,并按文檔規(guī)范返回應(yīng)答
注意:
更多參數(shù)、響應(yīng)詳情及錯誤碼請參見 JSAPI / APP / H5 / Native / 小程序支付通知API接口文檔
步驟說明:當(dāng)商戶后臺、網(wǎng)絡(luò)、服務(wù)器等出現(xiàn)異常,商戶系統(tǒng)最終未接收到支付通知時,商戶可通過查詢訂單接口核實訂單支付狀態(tài)
示例代碼(通過微信訂單號查詢):
public void QueryOrder() throws Exception {
//請求URL
URIBuilder uriBuilder = new URIBuilder("https://api.mch.weixin.qq.com/v3/pay/transactions/id/4200000745202011093730578574");
uriBuilder.setParameter("mchid", mchId);
//完成簽名并執(zhí)行請求
HttpGet httpGet = new HttpGet(uriBuilder.build());
httpGet.addHeader("Accept", "application/json");
CloseableHttpResponse response = httpClient.execute(httpGet);
try {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) {
System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
} else if (statusCode == 204) {
System.out.println("success");
} else {
System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
throw new IOException("request failed");
}
} finally {
response.close();
}
}
注意:
更多參數(shù)、響應(yīng)詳情及錯誤碼請參見 JSAPI / APP / H5 / Native / 小程序查詢訂單API接口文檔
步驟說明:當(dāng)商戶訂單支付失敗需要生成新單號重新發(fā)起支付,要對原訂單號調(diào)用關(guān)單,避免重復(fù)支付;系統(tǒng)下單后,用戶支付超時,系統(tǒng)退出不再受理,避免用戶繼續(xù),請調(diào)用關(guān)單接口
示例代碼:
public void CloseOrder() throws Exception {
//請求URL
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/sdkphp12345678920201028112429/close");
//請求body參數(shù)
String reqdata ="{\"mchid\": \""+mchId+"\"}";
StringEntity entity = new StringEntity(reqdata,"utf-8");
entity.setContentType("application/json");
httpPost.setEntity(entity);
httpPost.setHeader("Accept", "application/json");
//完成簽名并執(zhí)行請求
CloseableHttpResponse response = httpClient.execute(httpPost);
try {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) {
System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
} else if (statusCode == 204) {
System.out.println("success");
} else {
System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
throw new IOException("request failed");
}
} finally {
response.close();
}
}
注意:
更多參數(shù)、響應(yīng)詳情及錯誤碼請參見 JSAPI / APP / H5 / Native / 小程序接口文檔
步驟說明:微信支付按天提供交易賬單文件,商戶可以通過該接口獲取賬單文件的下載地址
示例代碼:
public void TradeBill() throws Exception {
//請求URL
URIBuilder uriBuilder = new URIBuilder("https://api.mch.weixin.qq.com/v3/bill/tradebill");
uriBuilder.setParameter("bill_date", "2020-11-09");
uriBuilder.setParameter("bill_type", "ALL");
//完成簽名并執(zhí)行請求
HttpGet httpGet = new HttpGet(uriBuilder.build());
httpGet.addHeader("Accept", "application/json");
CloseableHttpResponse response = httpClient.execute(httpGet);
try {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) {
System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
} else if (statusCode == 204) {
System.out.println("success");
} else {
System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
throw new IOException("request failed");
}
} finally {
response.close();
}
}
注意:
更多參數(shù)、響應(yīng)詳情及錯誤碼請參見 JSAPI / APP / H5 / Native / 小程序接口文檔
步驟說明:通過申請交易賬單接口獲取到賬單下載地址(download_url)后,再通過該接口獲取到對應(yīng)的賬單文件,文件內(nèi)包含交易相關(guān)的金額、時間、營銷等信息,供商戶核對訂單、退款、銀行到賬等情況
示例代碼:
public void DownloadUrl(String download_url) throws Exception{
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes("utf-8")));
//初始化httpClient
//該接口無需進(jìn)行簽名驗證、通過withValidator((response) -> true)實現(xiàn)
httpClient = WechatPayHttpClientBuilder.create().withMerchant(mchId, mchSerialNo, merchantPrivateKey).withValidator((response) -> true).build();
//請求URL
//賬單文件的下載地址的有效時間為30s
URIBuilder uriBuilder = new URIBuilder(download_url);
HttpGet httpGet = new HttpGet(uriBuilder.build());
httpGet.addHeader("Accept", "application/json");
//執(zhí)行請求
CloseableHttpResponse response = httpClient.execute(httpGet);
try {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) {
System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
} else if (statusCode == 204) {
System.out.println("success");
} else {
System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
throw new IOException("request failed");
}
} finally {
response.close();
}
}
注意:
? 賬單文件的下載地址的有效時間為30s
? 強烈建議商戶將實際賬單文件的哈希值和之前從接口獲取到的哈希值進(jìn)行比對,以確認(rèn)數(shù)據(jù)的完整性
更多參數(shù)、響應(yīng)詳情及錯誤碼請參見 JSAPI / APP / H5 / Native / 小程序下載賬單API接口文檔
A:請按以下步驟進(jìn)行排查:
1. 建議檢查一下公眾號的功能。比如是不是在訂閱號/未認(rèn)證的公眾號里面嘗試調(diào)用認(rèn)證服務(wù)號的功能
2. 確認(rèn)APPID是否認(rèn)證過期或者APPID填寫錯誤
3. 請嘗試使用snsapi_userinfo的授權(quán)登錄方式
A:請按以下步驟進(jìn)行排查:
1. 請檢查你的下單接口是否指定了支付用戶的身份,該功能需單獨開通指定身份支付權(quán)限方可使用
2. 請確認(rèn)你使用的商戶號是否有jsapi支付的權(quán)限,可登錄商戶平臺-產(chǎn)品中心查看
A:請檢查下單接口中使用的商戶號是否在商戶平臺配置了對應(yīng)的支付目錄,可參考“設(shè)置支付目錄”章節(jié)說明
A:請按以下步驟進(jìn)行排查:
1. 檢查下單接口傳的appid與獲取openid接口的appid是否同一個(需一致)
2. 檢查appid對應(yīng)的公眾號后臺,是否配置的授權(quán)域名和獲取openid的域名一致。授權(quán)域名配置路徑:【公眾平臺-> 設(shè)置-> 公眾號設(shè)置-> 功能設(shè)置–> 網(wǎng)頁授權(quán)域名】
A:JSAPI支付只能從微信瀏覽器內(nèi)發(fā)起支付請求