支付寶開發 ——第三方支付
支付寶開發
第三方支付
線上支付
線上支付是指賣方與買方通過因特網上的電子商務網站進行交易時,銀行為其提供網上資金結算服務的一種業務。它為企業和個人提供了一個安全、快捷、方便的電子商務應用環境和網上資金結算工具。線上支付不僅幫助企業實現了銷售款項的快速歸集,縮短收款週期,同時也為個人網上銀行客戶提供了網上消費支付結算方式,使客戶真正做到足不出戶,網上購物。
聚合支付
聚合支付:也稱“融合支付”,是指只從事“支付、結算、清算”服務之外的“支付服務”,依託銀行、非銀機構或清算組織,藉助銀行、非銀機構或清算組織的支付通道與清結算能力,利用自身的技術與服務整合能力,將一個以上的銀行、非銀機構或清算組織的支付服務,整合到一起,為商戶提供包括但不限於“支付通道服務”、“集合對賬服務”、“技術對接服務”、“差錯處理服務”、“金融服務引導”、“會員賬戶服務”、“作業流程軟體服務”、“執行維護服務”、“終端提供與維護”等服務內容,以此減少商戶接入、維護支付結算服務時面臨的成本支出,提高商戶支付結算系統執行效率的,並收取增值收益的支付服務。
第三方支付流程
回撥方式:同步回撥、非同步回撥
回撥場景: 告訴商戶支付通知結果
同步回撥: 整個支付流程完畢,使用同步方式將引數重定向給商戶平臺,一般場景用於展示結果。
非同步回撥: 第三方支付介面發一個後臺通知給商戶平臺,一般場景使用者修改訂單資訊。
資料安全加密
單向加密
單向加密演算法主要用來驗證資料傳輸的過程中,是否被篡改過。
MD5(Message Digest algorithm 5,資訊摘要演算法)
SHA(Secure Hash Algorithm,安全雜湊演算法)
HMAC(Hash Message Authentication Code,雜湊訊息鑑別碼
對稱加密
你可以這麼理解,一方通過金鑰將資訊加密後,把密文傳給另一方,另一方通過這個相同的金鑰將密文解密,轉換成可以理解的明文。他們之間的關係如下:
明文 <-> 金鑰 <-> 密文
常用對稱加密方案 DES、AES、Base64
非對稱加密
在通訊雙方,如果使用非對稱加密,一般遵從這樣的原則:公鑰加密,私鑰解密。同時,一般一個金鑰加密,另一個金鑰就可以解密。
因為公鑰是公開的,如果用來解密,那麼就很容易被不必要的人解密訊息。因此,私鑰也可以認為是個人身份的證明。
如果通訊雙方需要互發訊息,那麼應該建立兩套非對稱加密的機制(即兩對公私鑰金鑰對),發訊息的一方使用對方的公鑰進行加密,接收訊息的一方使用自己的私鑰解密。
就從上面提到的這個對稱加密的缺點開始,怎麼做到即時一個人的金鑰被盜竊了,最起碼保證你給其他人傳送密文不被破解。於是,人們就想出了這麼個辦法,首先,我們停止分享共同的金鑰,因為上面的 bug 就是來源於共享一個金鑰,那麼怎麼辦呢?每個人生成一個“私鑰-公鑰”對,這個私鑰需要每個人自行進行保護!公鑰可以隨便分享,後面詳細說,同時,生成的這個“私鑰-公鑰”對還有個強大的功能就是,使用私鑰加密的資訊,只能由該私鑰對應的公鑰才能解密,使用公鑰加密的資訊,只能由該公鑰對應的私鑰才能解密!
常用非對稱加密 RSA 一般用於資料安全加密特別,支付領域。
引數驗籤
外網對映工具
Ngrok使用
●windows使用者:
1,下載windows版本的客戶端,解壓到你喜歡的目錄
2,在命令列下進入到path/to/windows_386/下
3,執行 ngrok -config=ngrok.cfg -subdomain xxx 80 //(xxx 是你自定義的域名字首)
4,如果開啟成功 你就可以使用 xxx.tunnel.qydev.com 來訪問你本機的 127.0.0.1:80 的服務啦
5,如果你自己有頂級域名,想通過自己的域名來訪問本機的專案,那麼先將自己的頂級域名解析到123.57.165.240(域名需要已備案哦),然後執行./ngrok -config=ngrok.cfg -hostname xxx.xxx.xxx 80 //(xxx.xxx.xxx是你自定義的頂級域名)
6,如果開啟成功 你就可以使用你的頂級域名來訪問你本機的 127.0.0.1:80 的服務啦
7,如果失敗 就加下交流群 反饋下問題 本屌會看看什麼原因....吧
Natapp使用
windows ,點選開始->執行->命令列提示符 後進入 natapp.exe的目錄
執行 natapp -authtoken= 175396706488ac93
支付寶開發環境
支付寶沙箱
螞蟻沙箱環境(Beta)是協助開發者進行介面功能開發及主要功能聯調的輔助環境。沙箱環境模擬了開放平臺部分產品的主要功能和主要邏輯(當前沙箱支援產品請參考“沙箱支援產品列表”)。
在開發者應用上線稽核前,開發者可以根據自身需求,先在沙箱環境中瞭解、組合和除錯各種開放介面,進行開發調通工作,從而幫助開發者在應用上線稽核完成後,能更快速、更順利的進行線上除錯和驗收工作。
如何使用和配置沙箱環境請參考《沙箱環境使用說明》。注意:
由於沙箱為模擬環境,在沙箱完成介面開發及主要功能除錯後,請務必在螞蟻正式環境進行完整的功能驗收測試。所有返回碼及業務邏輯以正式環境為準。
為保證沙箱穩定,沙箱環境測試資料會進行定期資料清理。Beta測試階段每週日中午12點至每週一中午12點為維護時間。在此時間內沙箱環境部分功能可能會不可用,敬請諒解。
請勿在沙箱進行壓力測試,以免觸發相應的限流措施,導致無法正常使用沙箱環境。
沙箱支援的各個開放產品,沙箱使用的特別說明請參考各產品的快速接入文件或技術接入文件章節。
https://openhome.alipay.com/platform/appDaily.htm
環境準備
修改AlipayConfig資訊
/* *
*類名:AlipayConfig
*功能:基礎配置類
*詳細:設定帳戶有關資訊及返回路徑
*修改日期:2017-04-05
*說明:
*以下程式碼只是為了方便商戶測試而提供的樣例程式碼,商戶可以根據自己網站的需要,按照技術文件編寫,並非一定要使用該程式碼。
*該程式碼僅供學習和研究支付寶介面使用,只是提供一個參考。
*/
public class AlipayConfig {
//↓↓↓↓↓↓↓↓↓↓請在這裡配置您的基本資訊↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
// 應用ID,您的APPID,收款賬號既是您的APPID對應支付寶賬號
public static String app_id = "2016092100564758";
// 商戶私鑰,您的PKCS8格式RSA2私鑰
public static String merchant_private_key = "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCuXtPexot6F4hVXEivfpCL/uiV38yvPc2RqOiQ2AeZFmBuc+CMT4oytiegKGqxEURYYmKFA/K6/n3BW4FMMx3jEW+n/gQC6uUxD2LWDmorJC14xv2B34UjdRXwneNh5GKD5ZrH6AH4bh2urNscxV9pOZlfdcqa99uT00oUjSlk3B3XYxJoqbxkuXwCkUv8IGrgAmM5pLOsoJF87fOzQ1l8UqGWT5GwuJmJZBXM5UBUvjKzp+H7BmIf0GU9dh6o4LpQdiX8wfDWGfqKhiLPHqa66+U7b+lsDh2pu+daYYhhqyjTAyL3epOhmMhEG7zCcT0PQj4WPtaYRDMVD6E6IKBzAgMBAAECggEAfw5l/6xYsYw4MUrfQ9FLblc+DwdWVFMKWZrka7aeQrSFa7ZP5q2Gm9ETKqaIp6FXVbfK5fWshwkthRkyK94LZwurepOjRKT4gDkf4a37OphP8fO9gUbn8qA8bmn957TM/CLwF748wVMrmb4mot5G2Zu44FAqY6U7gImzLyp1ASITdlDbl1FU0Zukgsk78/rRipMVqeeMvHCwZdJPzrDX0MhNVcivBgJF7u2d49Ypm7PvvCn2OHLtiiKvyLbqkK7r3cR5Si/0aaMsFwvrPbd0a+QJpYDtU/ry+kw8izqcBTC1u4inPl8Fa9F/1qjAYUskCOyOXIfOoKbBzku5nO+VMQKBgQD3YIj+2fle1t8eCk87OVdltNdXwoduld+Xee4WiV4W2WBfHDOk81usc8R/wGl5p6AJpoVT4wVaaDYWqjppAd/RweBlfG9wGH5ytXWrys9AcViC4bIRnQmI6+pHFFMSaRDfVYHdxqzQXUgL0+2GPCqxY1aDJJFAVcLL1CjDrfXLdQKBgQC0ctGeFyYRDOQUiP2SY7Drqke+yisN79vfZd7SZ41UYf6AzSBesi8iaEdag7qhEMcVGQxetnadOXN2hC/jMy5CBnCpBHLjwcfNNe8egEqKu9KiKwZAmMwVbSSV3adon6818YWASxz5pHN/VbolW+VFoH0IjU/n70FkkUYH7TEHRwKBgF394u+aWKLNV6ctWZ9yESAGz098DUNaVMNUQ79yYDqkS3a323OQN8PVlNLJhAoCQ8+G4t/VwWHxeKOx+FGPscAcPyuwVRMta1YgVl54x7h/mJbaNHN2zHmm0bRCJ7I2E4AYGCjw5Raias57rqMzVzFhQizAByR/sW0K5pY7EcpFAoGANZVm46ASILwIOoTXb5IE5mZBOcmE8XWJgBQbD7XKRQV4crz24MfesUPv9FPrpop546z1fGaIYHW/8LCeG8SF9vs8lyQIDdPsRea/I/qKqBnQGXHXQHVfHPm1BH+2h53rhIQ81XT4nLUVyvkk9pUMRxm6J0D3OnNUos1000O+7F8CgYBpQB1YGhAw+c0ludeFmvyglHEL+dQxfnGCKWe3DseURG0gVCH8Ty7o+eB5CrbGgZobw6GJG0YcNBv1GqkloodJ3Rgdiikvpu7UJ005LOZxOjmqIvKiYgFJd9Fe2kLkoOpcfrdpc8pQrTDEKtGX8hHvP1/2zfEQAo6Mcnz8layS8g==";
// 支付寶公鑰,檢視地址:https://openhome.alipay.com/platform/keyManage.htm 對應APPID下的支付寶公鑰。
public static String alipay_public_key = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDIgHnOn7LLILlKETd6BFRJ0GqgS2Y3mn1wMQmyh9zEyWlz5p1zrahRahbXAfCfSqshSNfqOmAQzSHRVjCqjsAw1jyqrXaPdKBmr90DIpIxmIyKXv4GGAkPyJ/6FTFY99uhpiq0qadD/uSzQsefWo0aTvP/65zi3eof7TcZ32oWpwIDAQAB";
// 伺服器非同步通知頁面路徑 需http://格式的完整路徑,不能加?id=123這類自定義引數,必須外網可以正常訪問
public static String notify_url = "http://itmayiedu.tunnel.qydev.com/notify_url.jsp";
// 頁面跳轉同步通知頁面路徑 需http://格式的完整路徑,不能加?id=123這類自定義引數,必須外網可以正常訪問
public static String return_url = "http://itmayiedu.tunnel.qydev.com/return_url.jsp";
// 簽名方式
public static String sign_type = "RSA";
// 字元編碼格式
public static String charset = "utf-8";
// 支付寶閘道器
public static String gatewayUrl = "https://openapi.alipaydev.com/gateway.do";
// 支付寶閘道器
public static String log_path = "C:\\";
//↑↑↑↑↑↑↑↑↑↑請在這裡配置您的基本資訊↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
/**
* 寫日誌,方便測試(看網站需求,也可以改成把記錄存入資料庫)
* @param sWord 要寫入日誌裡的文字內容
*/
public static void logResult(String sWord) {
FileWriter writer = null;
try {
writer = new FileWriter(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();
}
}
}
}
}
執行Demo專案
專案使用整合支付寶
支付資料庫表結構
CREATE TABLE `payment_info` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`userid` int(11) DEFAULT NULL,
`typeid` int(2) DEFAULT NULL,
`orderid` varchar(50) DEFAULT NULL,
`price` decimal(10,0) DEFAULT NULL,
`source` varchar(10) DEFAULT NULL,
`state` int(2) DEFAULT NULL,
`created` datetime DEFAULT NULL,
`updated` datetime DEFAULT NULL,
`platformorderid` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
建立itmayiedu-shopp-pay服務工程
application配置檔案
server:
port: 8768
# context-path: /member
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
spring:
application:
name: pay
redis:
host: 47.100.50.60
password: 123
port: 6379
pool:
max-idle: 100
min-idle: 1
max-active: 1000
max-wait: -1
#資料庫連線資訊
datasource:
name: test
url: jdbc:mysql://127.0.0.1:3306/itmayiedu-pay
username: root
password: root
# 使用druid資料來源
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
filters: stat
maxActive: 20
initialSize: 1
maxWait: 60000
minIdle: 1
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxOpenPreparedStatements: 20
資料庫訪問層
@Mapper
public interface PaymentInfoDao {
@Select("select * from payment_info where id=#{id}")
public PaymentInfo getPaymentInfo(@Param("id") Long id);
@Insert("insert into payment_info ( id,userid,typeid,orderid,platformorderid,price,source,state,created,updated) value(null,#{userId},#{typeId},#{orderId},#{platformorderId},#{price},#{source},#{state},#{created},#{updated})")
@Options(useGeneratedKeys = true, keyProperty = "id") // 新增該行,product中的id將被自動新增
public Integer savePaymentType(PaymentInfo paymentInfo);
@Select("select * from payment_info where orderId=#{orderId}")
public PaymentInfo getByOrderIdPayInfo(@Param("orderId") String orderId);
@Update("update payment_info set state =#{state},payMessage=#{payMessage},platformorderId=#{platformorderId},updated=#{updated} where orderId=#{orderId} ")
public void updatePayInfo(PaymentInfo paymentInfo);
}
業務邏輯層
@Service
public class AliBaBaManagerImpl implements PayManager {
@Override
public String payInfo(PaymentInfo paymentInfo) throws AlipayApiException {
//獲得初始化的AlipayClient
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);
//設定請求引數
AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
alipayRequest.setReturnUrl(AlipayConfig.return_url);
alipayRequest.setNotifyUrl(AlipayConfig.notify_url);
//商戶訂單號,商戶網站訂單系統中唯一訂單號,必填
String out_trade_no =paymentInfo.getOrderId();
//付款金額,必填
String total_amount = paymentInfo.getPrice()+"";
//訂單名稱,必填
String subject ="螞蟻課堂會員費用";
// //商品描述,可空
// String body = new String(request.getParameter("WIDbody").getBytes("ISO-8859-1"),"UTF-8");
alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
+ "\"total_amount\":\""+ total_amount +"\","
+ "\"subject\":\""+ subject +"\","
+ "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
//若想給BizContent增加其他可選請求引數,以增加自定義超時時間引數timeout_express來舉例說明
//alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
// + "\"total_amount\":\""+ total_amount +"\","
// + "\"subject\":\""+ subject +"\","
// + "\"body\":\""+ body +"\","
// + "\"timeout_express\":\"10m\","
// + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
//請求引數可查閱【電腦網站支付的API文件-alipay.trade.page.pay-請求引數】章節
//請求
String result = alipayClient.pageExecute(alipayRequest).getBody();
return result;
}
}
服務實現層
@RestController
public class PayServiceImpl extends BaseApiService implements PayService {
@Autowired
private PaymentInfoDao paymentInfoDao;
@Autowired
private AliBaBaManagerImpl aliBaBaManagerImpl;
@Override
public ResponseBase getPayToken(@RequestBody PaymentInfo paymentInfo) {
// 1.插入引數提交資訊
Integer savePaymentType = paymentInfoDao.savePaymentType(paymentInfo);
if (savePaymentType <= 0) {
return setResultError("系統錯誤!");
}
// 2.生成對應token
String payToken = TokenUtils.getPayToken();
// 3.存放在redis中
Integer payId = paymentInfo.getId();
baseRedisService.setString(payToken, payId + "", Constants.PAY_TOKEN_MEMBER_TIME);
// 4.返回token給消費者
JSONObject result = new JSONObject();
result.put("payToken", payToken);
return setResultSuccess(result);
}
@Override
public ResponseBase payInfo(String payToken) {
if (StringUtils.isEmpty(payToken)) {
return setResultError("token不能為空!");
}
String payId = (String) baseRedisService.getString(payToken);
if (StringUtils.isEmpty(payId)) {
return setResultError("支付已經超時!");
}
PaymentInfo paymentInfo = paymentInfoDao.getPaymentInfo(Long.parseLong(payId));
if (paymentInfo == null) {
return setResultError("未找到交易型別.");
}
// 判斷型別 呼叫 具體業務介面
Long typeId = paymentInfo.getTypeId();
PayManager payManager = null;
// 呼叫支付介面
if (typeId == 1) {
payManager = aliBaBaManagerImpl;
}
try {
String payInfo = payManager.payInfo(paymentInfo);
JSONObject payInfoJSON = new JSONObject();
payInfoJSON.put("payInfo", payInfo);
return setResultSuccess(payInfoJSON);
} catch (AlipayApiException e) {
return setResultError("支付錯誤!");
}
}
}
專案啟動
@SpringBootApplication
@EnableEurekaClient
public class PayApp {
public static void main(String[] args) {
SpringApplication.run(PayApp.class, args);
}
}