Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
于如何允許客戶使用他們的 Metamask 錢包進(jìn)行支付的分步指南。
因此,假設(shè)您有一家在線商店,并希望允許您的客戶使用加密貨幣進(jìn)行購(gòu)買。 在本文中,我們將完成使用 Javascript 和 Metamask 實(shí)現(xiàn)簡(jiǎn)單的加密貨幣支付流程的步驟。
整個(gè)流程將如下所示:
支付流程
執(zhí)行支付的流程如下:
檢查用戶是否登錄
首先,當(dāng)頁(yè)面第一次加載時(shí),我們要檢查用戶是否已經(jīng)將錢包連接到應(yīng)用程序。 為此,我們只需使用“eth_accounts”方法獲取用戶的帳戶。 如果未返回任何帳戶,則表示用戶未連接。 這是執(zhí)行此操作的代碼:
function checkIfWalletConnected() {
if (window.ethereum.request({ method: 'eth_accounts' }).then(function (accounts) {
if (accounts.length > 0) {
connected=true;
buyerAddress=accounts[0];
} else {
connected=false;
}
})
) {
connected=true;
} else {
connected=false;
}
}
如果用戶已登錄,我們會(huì)顯示結(jié)帳菜單,如果沒(méi)有,我們會(huì)顯示一個(gè)連接按鈕。
將用戶連接到應(yīng)用程序
如果用戶未連接,我們需要為連接按鈕分配一個(gè)功能,以將錢包連接到應(yīng)用程序。 這可以通過(guò)使用“eth_requestAccounts”方法來(lái)實(shí)現(xiàn)。 此方法將創(chuàng)建一個(gè) Metamask 彈出窗口,供用戶簽署消息并確認(rèn)他想要連接到應(yīng)用程序。
這是此類功能的代碼:
function connectWallet() {
if (window.ethereum) {
console.log('MetaMask is installed');
window.web3=new Web3(window.ethereum);
window.ethereum.send('eth_requestAccounts').then(function() {
// Get account address
window.ethereum.request({ method: 'eth_accounts' })
.then(function(accounts) {
if (accounts.length > 0) {
buyerAddress=accounts[0];
} else {
connected=false;
}
});
});
} else if (window.web3) {
window.web3=new Web3(window.web3.currentProvider);
connected=true;
} else {
connected=false;
}
}
用戶現(xiàn)在將連接到網(wǎng)站并能夠進(jìn)行結(jié)帳。
處理付款
對(duì)于最后一部分,我們希望提示用戶通過(guò) Metamask 確認(rèn)交易。 這可以通過(guò)“eth_sendTransaction”方法來(lái)實(shí)現(xiàn):
function makePaymentRequest(buyerAddress, sellerAddress, itemPriceInWei) {
// Start wallet payment process
window.ethereum.request({ method: 'eth_sendTransaction', params: [{ from: buyerAddress, to: sellerAddress, value: itemPriceInWei }] })
.then(response=> {
console.log(response);
return true;
})
.catch(error=> {
console.log(error);
return false;
});
}
完整的代碼片段
此代碼的完整工作版本可以在下面的鏈接中看到。 該片段使用 Vue.js 和 Tailwindcss 來(lái)實(shí)現(xiàn)本文開(kāi)頭所示的接口。
結(jié)論
本文是使用 Metamask 快速實(shí)施基本區(qū)塊鏈支付系統(tǒng)的簡(jiǎn)短實(shí)用指南。
這是最簡(jiǎn)單的實(shí)現(xiàn),但如果您想跟蹤客戶和訂單,不建議這樣做。 如果您有興趣學(xué)習(xí)如何創(chuàng)建一個(gè)更強(qiáng)大的架構(gòu)來(lái)處理訂單跟蹤和更強(qiáng)大的流程,請(qǐng)關(guān)注我,因?yàn)槲覍⒑芸彀l(fā)布一篇關(guān)于我如何使用無(wú)服務(wù)器云功能創(chuàng)建全棧實(shí)現(xiàn)的文章。
快樂(lè)編碼!
近公司在做支付模塊,在接入過(guò)程中遇到了很多坑,費(fèi)了不少事,現(xiàn)在分享一下接入方法,也記錄一下,以后可能還用的到。用的是支付寶的即時(shí)到帳支付功能和微信的掃碼支付功能,相比起來(lái),個(gè)人感覺(jué)支付寶的文檔和接入方式都比微信的容易理解和操作,也不用自己寫頁(yè)面,接入起來(lái)比較方便,畢竟是支付起家的,比微信支付少很多坑,下面就分別介紹著兩種支付的接入方法。
支付寶支付
1、申請(qǐng)簽約
目的是得到開(kāi)發(fā)使用的合作伙伴身份(PID)和MD5秘鑰,申請(qǐng)地址(即時(shí)到賬收款):https://b.alipay.com/order/productDetail.htm?productId=2015110218012942
申請(qǐng)方式在開(kāi)放平臺(tái)的文檔上有詳細(xì)說(shuō)明,這里就不再贅述。
2、接入支付接口
在得到PID和秘鑰后就可以接入接口了,首先在開(kāi)放平臺(tái)中下載官方的demo(java+MD5版本),支付寶的demo做的非常好,下載下來(lái)直接配置下jdk就可以運(yùn)行了。如果遇到Java compiler level does not match錯(cuò)誤,說(shuō)明你用的eclipse或myeclipse的jdk編譯版本與demo的JDK編譯版本不一致,修改下jdk編譯版本就可以了。其實(shí)就用到了4個(gè)類,如下圖
可以選擇把支付功能單獨(dú)做一個(gè)項(xiàng)目,在其他項(xiàng)目調(diào)用接口就可以支付,也可以整合到自己的項(xiàng)目里,為了好維護(hù)我整合到自己的項(xiàng)目里了。把這四個(gè)類放到自己的項(xiàng)目中,引入相應(yīng)的jar包
2.1、demo中類的說(shuō)明
AlipayConfig.java類主要是配置參數(shù)信息的類
package com.fahai.pay.alipay;
import com.fahai.utils.ProInfoUtil;
/* *
*類名:AlipayConfig
*功能:基礎(chǔ)配置類
*詳細(xì):設(shè)置帳戶有關(guān)信息及返回路徑
*版本:3.4
*修改日期:2016-03-08
*說(shuō)明:
*以下代碼只是為了方便商戶測(cè)試而提供的樣例代碼,商戶可以根據(jù)自己網(wǎng)站的需要,按照技術(shù)文檔編寫,并非一定要使用該代碼。
*該代碼僅供學(xué)習(xí)和研究支付寶接口使用,只是提供一個(gè)參考。
*/
public class AlipayConfig {
//↓↓↓↓↓↓↓↓↓↓請(qǐng)?jiān)谶@里配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
// 合作身份者ID,簽約賬號(hào),以2088開(kāi)頭由16位純數(shù)字組成的字符串,查看地址:https://b.alipay.com/order/pidAndKey.htm
public static String partner="你自己的PID";
// 收款支付寶賬號(hào),以2088開(kāi)頭由16位純數(shù)字組成的字符串,一般情況下收款賬號(hào)就是簽約賬號(hào)
public static String seller_id=partner;
// MD5密鑰,安全檢驗(yàn)碼,由數(shù)字和字母組成的32位字符串,查看地址:https://b.alipay.com/order/pidAndKey.htm
public static String key="你自己的MD5秘鑰";
// 服務(wù)器異步通知頁(yè)面路徑 需http://格式的完整路徑,不能加?id=123這類自定義參數(shù),必須外網(wǎng)可以正常訪問(wèn)
//異步通知頁(yè)面,就是接受支付寶支付結(jié)果返回信息的Controller,可以處理自己的支付后的邏輯
//測(cè)試環(huán)境
public static String notify_url=ProInfoUtil.getInstance().getProperty("project_url")+"order/pay/aliPayOrder";
// 頁(yè)面跳轉(zhuǎn)同步通知頁(yè)面路徑 需http://格式的完整路徑,不能加?id=123這類自定義參數(shù),必須外網(wǎng)可以正常訪問(wèn)
//同步跳轉(zhuǎn)的頁(yè)面,就是支付寶支付成功后頁(yè)面跳轉(zhuǎn)的url
public static String return_url=ProInfoUtil.getInstance().getProperty("project_url")+"order/pay/payResponse";
// 簽名方式
public static String sign_type="MD5";
// 調(diào)試用,創(chuàng)建TXT日志文件夾路徑,見(jiàn)AlipayCore.java類中的logResult(String sWord)打印方法。
public static String log_path="C:\";
// 字符編碼格式 目前支持 gbk 或 utf-8
public static String input_charset="utf-8";
// 支付類型 ,無(wú)需修改
public static String payment_type="1";
// 調(diào)用的接口名,無(wú)需修改
public static String service="create_direct_pay_by_user";
//↑↑↑↑↑↑↑↑↑↑請(qǐng)?jiān)谶@里配置您的基本信息↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
//↓↓↓↓↓↓↓↓↓↓ 請(qǐng)?jiān)谶@里配置防釣魚信息,如果沒(méi)開(kāi)通防釣魚功能,為空即可 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
// 防釣魚時(shí)間戳 若要使用請(qǐng)調(diào)用類文件submit中的query_timestamp函數(shù)
public static String anti_phishing_key="";
// 客戶端的IP地址 非局域網(wǎng)的外網(wǎng)IP地址,如:221.0.0.1
public static String exter_invoke_ip="";
//↑↑↑↑↑↑↑↑↑↑請(qǐng)?jiān)谶@里配置防釣魚信息,如果沒(méi)開(kāi)通防釣魚功能,為空即可 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
}
AlipayCore.java是整理參數(shù)的工具類
package com.alipay.util;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.httpclient.methods.multipart.FilePartSource;
import org.apache.commons.httpclient.methods.multipart.PartSource;
import com.alipay.config.AlipayConfig;
/* *
*類名:AlipayFunction
*功能:支付寶接口公用函數(shù)類
*詳細(xì):該類是請(qǐng)求、通知返回兩個(gè)文件所調(diào)用的公用函數(shù)核心處理文件,不需要修改
*版本:3.3
*日期:2012-08-14
*說(shuō)明:
*以下代碼只是為了方便商戶測(cè)試而提供的樣例代碼,商戶可以根據(jù)自己網(wǎng)站的需要,按照技術(shù)文檔編寫,并非一定要使用該代碼。
*該代碼僅供學(xué)習(xí)和研究支付寶接口使用,只是提供一個(gè)參考。
*/
public class AlipayCore {
/**
* 除去數(shù)組中的空值和簽名參數(shù)
* @param sArray 簽名參數(shù)組
* @return 去掉空值與簽名參數(shù)后的新簽名參數(shù)組
*/
public static Map<String, String> paraFilter(Map<String, String> sArray) {
Map<String, String> result=new HashMap<String, String>();
if (sArray==null || sArray.size() <=0) {
return result;
}
for (String key : sArray.keySet()) {
String value=sArray.get(key);
if (value==null || value.equals("") || key.equalsIgnoreCase("sign")
|| key.equalsIgnoreCase("sign_type")) {
continue;
}
result.put(key, value);
}
return result;
}
/**
* 把數(shù)組所有元素排序,并按照“參數(shù)=參數(shù)值”的模式用“&”字符拼接成字符串
* @param params 需要排序并參與字符拼接的參數(shù)組
* @return 拼接后字符串
*/
public static String createLinkString(Map<String, String> params) {
List<String> keys=new ArrayList<String>(params.keySet());
Collections.sort(keys);
String prestr="";
for (int i=0; i < keys.size(); i++) {
String key=keys.get(i);
String value=params.get(key);
if (i==keys.size() - 1) {//拼接時(shí),不包括最后一個(gè)&字符
prestr=prestr + key + "=" + value;
} else {
prestr=prestr + key + "=" + value + "&";
}
}
return prestr;
}
/**
* 寫日志,方便測(cè)試(看網(wǎng)站需求,也可以改成把記錄存入數(shù)據(jù)庫(kù))
* @param sWord 要寫入日志里的文本內(nèi)容
*/
public static void logResult(String sWord) {
FileWriter writer=null;
try {
writer=new FileWriter(AlipayConfig.log_path + "alipay_log_" + System.currentTimeMillis()+".txt");
writer.write(sWord);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (writer !=null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 生成文件摘要
* @param strFilePath 文件路徑
* @param file_digest_type 摘要算法
* @return 文件摘要結(jié)果
*/
public static String getAbstract(String strFilePath, String file_digest_type) throws IOException {
PartSource file=new FilePartSource(new File(strFilePath));
if(file_digest_type.equals("MD5")){
return DigestUtils.md5Hex(file.createInputStream());
}
else if(file_digest_type.equals("SHA")) {
return DigestUtils.sha256Hex(file.createInputStream());
}
else {
return "";
}
}
}
AlipayNotify.java是驗(yàn)證簽名的類
package com.alipay.util;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;
import com.alipay.config.AlipayConfig;
import com.alipay.sign.MD5;
/* *
*類名:AlipayNotify
*功能:支付寶通知處理類
*詳細(xì):處理支付寶各接口通知返回
*版本:3.3
*日期:2012-08-17
*說(shuō)明:
*以下代碼只是為了方便商戶測(cè)試而提供的樣例代碼,商戶可以根據(jù)自己網(wǎng)站的需要,按照技術(shù)文檔編寫,并非一定要使用該代碼。
*該代碼僅供學(xué)習(xí)和研究支付寶接口使用,只是提供一個(gè)參考
*************************注意*************************
*調(diào)試通知返回時(shí),可查看或改寫log日志的寫入TXT里的數(shù)據(jù),來(lái)檢查通知返回是否正常
*/
public class AlipayNotify {
/**
* 支付寶消息驗(yàn)證地址
*/
private static final String HTTPS_VERIFY_URL="https://mapi.alipay.com/gateway.do?service=notify_verify&";
/**
* 驗(yàn)證消息是否是支付寶發(fā)出的合法消息
* @param params 通知返回來(lái)的參數(shù)數(shù)組
* @return 驗(yàn)證結(jié)果
*/
public static boolean verify(Map<String, String> params) {
//判斷responsetTxt是否為true,isSign是否為true
//responsetTxt的結(jié)果不是true,與服務(wù)器設(shè)置問(wèn)題、合作身份者ID、notify_id一分鐘失效有關(guān)
//isSign不是true,與安全校驗(yàn)碼、請(qǐng)求時(shí)的參數(shù)格式(如:帶自定義參數(shù)等)、編碼格式有關(guān)
String responseTxt="false";
if(params.get("notify_id") !=null) {
String notify_id=params.get("notify_id");
responseTxt=verifyResponse(notify_id);
}
String sign="";
if(params.get("sign") !=null) {sign=params.get("sign");}
boolean isSign=getSignVeryfy(params, sign);
//寫日志記錄(若要調(diào)試,請(qǐng)取消下面兩行注釋)
//String sWord="responseTxt=" + responseTxt + "\n isSign=" + isSign + "\n 返回回來(lái)的參數(shù):" + AlipayCore.createLinkString(params);
//AlipayCore.logResult(sWord);
if (isSign && responseTxt.equals("true")) {
return true;
} else {
return false;
}
}
/**
* 根據(jù)反饋回來(lái)的信息,生成簽名結(jié)果
* @param Params 通知返回來(lái)的參數(shù)數(shù)組
* @param sign 比對(duì)的簽名結(jié)果
* @return 生成的簽名結(jié)果
*/
private static boolean getSignVeryfy(Map<String, String> Params, String sign) {
//過(guò)濾空值、sign與sign_type參數(shù)
Map<String, String> sParaNew=AlipayCore.paraFilter(Params);
//獲取待簽名字符串
String preSignStr=AlipayCore.createLinkString(sParaNew);
//獲得簽名驗(yàn)證結(jié)果
boolean isSign=false;
if(AlipayConfig.sign_type.equals("MD5") ) {
isSign=MD5.verify(preSignStr, sign, AlipayConfig.key, AlipayConfig.input_charset);
}
return isSign;
}
/**
* 獲取遠(yuǎn)程服務(wù)器ATN結(jié)果,驗(yàn)證返回URL
* @param notify_id 通知校驗(yàn)ID
* @return 服務(wù)器ATN結(jié)果
* 驗(yàn)證結(jié)果集:
* invalid命令參數(shù)不對(duì) 出現(xiàn)這個(gè)錯(cuò)誤,請(qǐng)檢測(cè)返回處理中partner和key是否為空
* true 返回正確信息
* false 請(qǐng)檢查防火墻或者是服務(wù)器阻止端口問(wèn)題以及驗(yàn)證時(shí)間是否超過(guò)一分鐘
*/
private static String verifyResponse(String notify_id) {
//獲取遠(yuǎn)程服務(wù)器ATN結(jié)果,驗(yàn)證是否是支付寶服務(wù)器發(fā)來(lái)的請(qǐng)求
String partner=AlipayConfig.partner;
String veryfy_url=HTTPS_VERIFY_URL + "partner=" + partner + "?ify_id=" + notify_id;
return checkUrl(veryfy_url);
}
/**
* 獲取遠(yuǎn)程服務(wù)器ATN結(jié)果
* @param urlvalue 指定URL路徑地址
* @return 服務(wù)器ATN結(jié)果
* 驗(yàn)證結(jié)果集:
* invalid命令參數(shù)不對(duì) 出現(xiàn)這個(gè)錯(cuò)誤,請(qǐng)檢測(cè)返回處理中partner和key是否為空
* true 返回正確信息
* false 請(qǐng)檢查防火墻或者是服務(wù)器阻止端口問(wèn)題以及驗(yàn)證時(shí)間是否超過(guò)一分鐘
*/
private static String checkUrl(String urlvalue) {
String inputLine="";
try {
URL url=new URL(urlvalue);
HttpURLConnection urlConnection=(HttpURLConnection) url.openConnection();
BufferedReader in=new BufferedReader(new InputStreamReader(urlConnection
.getInputStream()));
inputLine=in.readLine().toString();
} catch (Exception e) {
e.printStackTrace();
inputLine="";
}
return inputLine;
}
}
AlipaySubmit.java模擬form表單請(qǐng)求支付寶支付接口的類
package com.alipay.util;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import com.alipay.config.AlipayConfig;
import com.alipay.sign.MD5;
/* *
*類名:AlipaySubmit
*功能:支付寶各接口請(qǐng)求提交類
*詳細(xì):構(gòu)造支付寶各接口表單HTML文本,獲取遠(yuǎn)程HTTP數(shù)據(jù)
*版本:3.3
*日期:2012-08-13
*說(shuō)明:
*以下代碼只是為了方便商戶測(cè)試而提供的樣例代碼,商戶可以根據(jù)自己網(wǎng)站的需要,按照技術(shù)文檔編寫,并非一定要使用該代碼。
*該代碼僅供學(xué)習(xí)和研究支付寶接口使用,只是提供一個(gè)參考。
*/
public class AlipaySubmit {
/**
* 支付寶提供給商戶的服務(wù)接入網(wǎng)關(guān)URL(新)
*/
private static final String ALIPAY_GATEWAY_NEW="https://mapi.alipay.com/gateway.do?";
/**
* 生成簽名結(jié)果
* @param sPara 要簽名的數(shù)組
* @return 簽名結(jié)果字符串
*/
public static String buildRequestMysign(Map<String, String> sPara) {
String prestr=AlipayCore.createLinkString(sPara); //把數(shù)組所有元素,按照“參數(shù)=參數(shù)值”的模式用“&”字符拼接成字符串
String mysign="";
if(AlipayConfig.sign_type.equals("MD5") ) {
mysign=MD5.sign(prestr, AlipayConfig.key, AlipayConfig.input_charset);
}
return mysign;
}
/**
* 生成要請(qǐng)求給支付寶的參數(shù)數(shù)組
* @param sParaTemp 請(qǐng)求前的參數(shù)數(shù)組
* @return 要請(qǐng)求的參數(shù)數(shù)組
*/
private static Map<String, String> buildRequestPara(Map<String, String> sParaTemp) {
//除去數(shù)組中的空值和簽名參數(shù)
Map<String, String> sPara=AlipayCore.paraFilter(sParaTemp);
//生成簽名結(jié)果
String mysign=buildRequestMysign(sPara);
//簽名結(jié)果與簽名方式加入請(qǐng)求提交參數(shù)組中
sPara.put("sign", mysign);
sPara.put("sign_type", AlipayConfig.sign_type);
return sPara;
}
/**
* 建立請(qǐng)求,以表單HTML形式構(gòu)造(默認(rèn))
* @param sParaTemp 請(qǐng)求參數(shù)數(shù)組
* @param strMethod 提交方式。兩個(gè)值可選:post、get
* @param strButtonName 確認(rèn)按鈕顯示文字
* @return 提交表單HTML文本
*/
public static String buildRequest(Map<String, String> sParaTemp, String strMethod, String strButtonName) {
//待請(qǐng)求參數(shù)數(shù)組
Map<String, String> sPara=buildRequestPara(sParaTemp);
List<String> keys=new ArrayList<String>(sPara.keySet());
StringBuffer sbHtml=new StringBuffer();
sbHtml.append("<form id=\"alipaysubmit\" name=\"alipaysubmit\" action=\"" + ALIPAY_GATEWAY_NEW
+ "_input_charset=" + AlipayConfig.input_charset + "\" method=\"" + strMethod
+ "\">");
for (int i=0; i < keys.size(); i++) {
String name=(String) keys.get(i);
String value=(String) sPara.get(name);
sbHtml.append("<input type=\"hidden\" name=\"" + name + "\" value=\"" + value + "\"/>");
}
//submit按鈕控件請(qǐng)不要含有name屬性
sbHtml.append("<input type=\"submit\" value=\"" + strButtonName + "\" style=\"display:none;\"></form>");
sbHtml.append("<script>document.forms['alipaysubmit'].submit();</script>");
return sbHtml.toString();
}
/**
* 用于防釣魚,調(diào)用接口query_timestamp來(lái)獲取時(shí)間戳的處理函數(shù)
* 注意:遠(yuǎn)程解析XML出錯(cuò),與服務(wù)器是否支持SSL等配置有關(guān)
* @return 時(shí)間戳字符串
* @throws IOException
* @throws DocumentException
* @throws MalformedURLException
*/
public static String query_timestamp() throws MalformedURLException,
DocumentException, IOException {
//構(gòu)造訪問(wèn)query_timestamp接口的URL串
String strUrl=ALIPAY_GATEWAY_NEW + "service=query_timestamp&partner=" + AlipayConfig.partner + "&_input_charset" +AlipayConfig.input_charset;
StringBuffer result=new StringBuffer();
SAXReader reader=new SAXReader();
Document doc=reader.read(new URL(strUrl).openStream());
List<Node> nodeList=doc.selectNodes("http://alipay/*");
for (Node node : nodeList) {
// 截取部分不需要解析的信息
if (node.getName().equals("is_success") && node.getText().equals("T")) {
// 判斷是否有成功標(biāo)示
List<Node> nodeList1=doc.selectNodes("http://response/timestamp/*");
for (Node node1 : nodeList1) {
result.append(node1.getText());
}
}
}
return result.toString();
}
}
這幾個(gè)類調(diào)用支付寶接口的是AlipaySubmit,在網(wǎng)頁(yè)選好購(gòu)買的商品時(shí),在系統(tǒng)中生成訂單,然后進(jìn)行支付,瀏覽器跳轉(zhuǎn)到支付寶支付網(wǎng)站,Controller中代碼為:
/**
* 支付寶支付頁(yè)面
*
* @return
* @throws IOException
*/
@RequestMapping(value="/aliPay")
public void aliPay(HttpServletRequest request, HttpServletResponse response) throws IOException {
LOGGER.info("支付寶支付頁(yè)面");
//商戶訂單號(hào),商戶網(wǎng)站訂單系統(tǒng)中唯一訂單號(hào),必填
String orderNo=request.getParameter("orderNo");
//訂單名稱,必填
String subjectName=request.getParameter("subjectName");
//付款金額,必填
String total_fee=request.getParameter("fee");
//商品描述,可空
String body="法海風(fēng)控 " + subjectName;
if ("money".equals(body)) {
body="法海風(fēng)控 余額充值";
}
//把請(qǐng)求參數(shù)打包成map
Map<String, String> sParaTemp=new HashMap<String, String>();
sParaTemp.put("service", AlipayConfig.service);
sParaTemp.put("partner", AlipayConfig.partner);
sParaTemp.put("seller_id", AlipayConfig.seller_id);
sParaTemp.put("_input_charset", AlipayConfig.input_charset);
sParaTemp.put("payment_type", AlipayConfig.payment_type);
sParaTemp.put("notify_url", AlipayConfig.notify_url);
sParaTemp.put("return_url", AlipayConfig.return_url);
sParaTemp.put("anti_phishing_key", AlipayConfig.anti_phishing_key);
sParaTemp.put("exter_invoke_ip", AlipayConfig.exter_invoke_ip);
sParaTemp.put("out_trade_no", orderNo);
sParaTemp.put("subject", subjectName);
sParaTemp.put("total_fee", total_fee);
sParaTemp.put("body", body);
//其他業(yè)務(wù)參數(shù)根據(jù)在線開(kāi)發(fā)文檔,添加參數(shù).文檔地址:https://doc.open.alipay.com/doc2/detail.htm?spm=a219a.7629140.0.0.O9yorI&treeId=62&articleId=103740&docType=1
//如sParaTemp.put("參數(shù)名","參數(shù)值");
//建立請(qǐng)求
String sHtmlText=AlipaySubmit.buildRequest(sParaTemp,"get","確認(rèn)");
response.setHeader("Content-Type", "text/html; charset=UTF-8");
response.setCharacterEncoding("UTF-8");
PrintWriter out=response.getWriter();
System.out.println(sHtmlText);
out.println(sHtmlText);
}
請(qǐng)求aliPay會(huì)跳轉(zhuǎn)到支付寶支付頁(yè)面:
3、接收支付結(jié)果通知
在配置好notify_url之后,支付結(jié)果會(huì)請(qǐng)求相應(yīng)的接口,我的是order/pay/aliPayOrder,代碼如下
/**
* 支付寶支付訂單
* @return
* @throws IOException
*/
@ResponseBody
@RequestMapping(value="pay/aliPayOrder", method=RequestMethod.POST)
public void aliPayOrder(HttpServletRequest request,HttpServletResponse response) throws IOException {
LOGGER.info("支付訂單");
//從request中獲得參數(shù)Map,并返回可讀的Map
Map<String, String> params=RequestUtil.getParameterMap(request);
LOGGER.info(params.toString());
//驗(yàn)證支付寶簽名
boolean aliSign=AlipayNotify.verify(params);
if (aliSign) {//驗(yàn)證成功
//交易狀態(tài)
String tradeStatus=params.get("trade_status");
//訂單編號(hào)
String orderNo=params.get("out_trade_no");
//支付單號(hào)
String payNo=params.get("trade_no");
//支付賬號(hào)
String payAccount=params.get("buyer_email");
//支付金額
String totalFee=params.get("total_fee");
//收款支付寶賬號(hào)
String sellerId=params.get("seller_id");
if (Constant.ALIPAY_TRADE_SUCCESS.equals(tradeStatus)) {//支付寶支付狀態(tài)為成功
//驗(yàn)證支付寶返回信息與請(qǐng)求信息一致
if (ProInfoUtil.getInstance().getProperty("alipay_partner").equals(sellerId)) {
//訂單處理狀態(tài)
String orderHandleStatus="error";
//驗(yàn)證訂單未做支付處理
Order order=orderService.queryOrderByOrderNo(orderNo);
//訂單已支付
if (Constant.DEALSTATUS_PAY.equals(order.getDealStatus())) {
response.getWriter().print("success");
return;
}
if (null !=order && Double.parseDouble(totalFee)==order.getDealPrice() &&
Constant.DEALSTATUS_NOT_PAY.equals(order.getDealStatus())) {//驗(yàn)證金額是否和訂單一致
//更新訂單為已支付、更新用戶套餐余額、添加用戶充值記錄、添加用戶余額支出記錄
order.setDealStatus(Constant.DEALSTATUS_PAY);
order.setPayNo(payNo);
order.setPayType(Constant.ALIPAY);
order.setPayAccount(payAccount);
try {
//支付成功處理支付業(yè)務(wù)
boolean result=orderService.payOrder(order);
if (result) {
//成功后向支付寶返回成功標(biāo)志
LOGGER.info("支付寶支付成功");
orderHandleStatus="success";
response.getWriter().print("success");
}
} catch (Exception e) {
e.printStackTrace();
LOGGER.info("支付寶支付失敗");
response.getWriter().print("fail");
}
}
//添加支付信息
Map<String, Object> map=new HashMap<String, Object>();
map.put("params", params.toString());
map.put("payType", Constant.ALIPAY);
map.put("orderNo", orderNo);
map.put("handleStatus", orderHandleStatus);
orderService.addPayInfo(map);
}
}
} else {//驗(yàn)證失敗
LOGGER.info("支付寶返回驗(yàn)證失敗");
response.getWriter().print("fail");
}
}
/**
* 從request中獲得參數(shù)Map,并返回可讀的Map
*
* @param request
* @return
*/
@SuppressWarnings("unchecked")
public static Map getParameterMap(HttpServletRequest request) {
// 參數(shù)Map
Map properties=request.getParameterMap();
// 返回值Map
Map<String, String> returnMap=new HashMap<String, String>();
Iterator entries=properties.entrySet().iterator();
Map.Entry entry;
String name="";
String value="";
while (entries.hasNext()) {
entry=(Map.Entry) entries.next();
name=(String) entry.getKey();
Object valueObj=entry.getValue();
if(null==valueObj){
value="";
}else if(valueObj instanceof String[]){
String[] values=(String[])valueObj;
for(int i=0;i<values.length;i++){
value=values[i] + ",";
}
value=value.substring(0, value.length()-1);
}else{
value=valueObj.toString();
}
returnMap.put(name, value);
}
return returnMap;
}
至此,支付寶支付功能已經(jīng)做完了,其中有幾個(gè)細(xì)節(jié)需要添加,比如支付時(shí)查詢訂單狀態(tài)是否已經(jīng)支付,是否過(guò)期等等,可以根據(jù)自己的需求去完善。
支付寶的接入還是很順利的,如果熟練的話一兩天就可以完成了,剛開(kāi)始寫博客,有錯(cuò)誤或者不明白的地方歡迎大家指出一起交流學(xué)習(xí),共同進(jìn)步。
由于篇幅問(wèn)題,我在下一章介紹微信支付的接入。
Dreamweaver中實(shí)現(xiàn)掃描二維碼支付,支付成功后進(jìn)入指定功能頁(yè)面,通常需要以下幾個(gè)步驟:
在Dreamweaver中,你可以使用HTML、CSS和JavaScript來(lái)創(chuàng)建用戶界面,并通過(guò)AJAX與服務(wù)器進(jìn)行交互。以下是一個(gè)簡(jiǎn)化的示例流程:
請(qǐng)注意,這只是一個(gè)簡(jiǎn)化的示例,實(shí)際的實(shí)現(xiàn)可能會(huì)更復(fù)雜,需要考慮安全性、用戶體驗(yàn)和錯(cuò)誤處理等因素。此外,具體的實(shí)現(xiàn)細(xì)節(jié)會(huì)根據(jù)你使用的支付平臺(tái)和后端技術(shù)棧有所不同。在實(shí)際開(kāi)發(fā)中,你可能需要查閱支付平臺(tái)的開(kāi)發(fā)文檔,了解如何生成二維碼、處理支付回調(diào)以及如何與前端頁(yè)面進(jìn)行交互。有需要完整源碼的朋友,加關(guān)注線下溝通。
附:代碼僅供需要的朋友參考(為便于展示顯示圖片效果)
支付成功后,微信異步回調(diào)頁(yè)面為notify_wxpay.php,在根目錄中,可根據(jù)情況修改
微信支付功能開(kāi)發(fā)
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。