PCI 標準是爲了最大限度保護持卡人數據的一套標準。要求不少,能夠看 PCI標準 站點了解。對於程序猿來講,要保證的是用戶的任何支付信息,都不走本身的服務器,不保存在本身的數據庫。javascript
實現符合PCI標準的支付,有兩種方式php
加載Authorize.net的託管表單html
使用AcceptJsjava
Authorize.net的託管表單,加載方便,安全性高,可是用戶定製程度不高,只能稍微改改表單樣式,AcceptJs
能夠使用本身設計的表單,調用AcceptJs
作安全性校驗和數據發送接收。git
沙盒環境帳號,能夠用來在api文檔頁面直接調試各類接口,也能夠在沙盒裏面查看各類扣款記錄。github
若是項目要上線,請註冊生產環境帳號,這裏所有使用沙盒環境。web
下載SDK到項目。數據庫
cd /your_php_project_path composer require authorizenet/authorizenet
再在項目中引入便可(如何引入能夠看上面地址的介紹,這裏再也不重複)。json
該項目的GITHUB地址:AuthorizeNet/sdk-php 能夠在上面搜索、提出你的issuesapi
使用SDK的php案列:AuthorizeNet/sample-code-php
Authorizenet官方實現的一個符合PCI標準的案列AuthorizeNet/accept-sample-app (這個沒有使用SDK)
由於Authorize.net SDK 要求 php: >=5.5 , 因此只能本身封裝api請求了,具體如何封裝我的自便,但要說明的一點是,Authorize.net 的api,若是選擇的是json格式:
header("Content-type:text/json;charset=utf-8"); $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $this->authorizeUrl); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_COOKIESESSION, true); curl_setopt($curl, CURLOPT_HEADER, 0); curl_setopt($curl, CURLOPT_POST, 1); curl_setopt($curl, CURLOPT_POSTFIELDS, urldecode($data)); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2); // curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: text/plain')); //xml request curl_setopt($curl, CURLOPT_HTTPHEADER, array('Accept: application/json')); $result = curl_exec($curl); $curlErrno = curl_errno($curl); $curlError = curl_error($curl); curl_close($curl);
返回的數據也是JSON
格式,but。。。。,這個返回的json數據,是沒法用
json_decode($result,true)
來解析的,須要
json_decode(substr($result, 3), true);
來解析。究其緣由,應該是它返回的數據帶了BOM
頭,詳細請移步 json-decode-returns-null
XML
格式我沒有去寫代碼測試,各位有興趣能夠自行測試,也能夠在沙盒環境直接測試。
有個直接扣款的API,其中的ORDER參數要有順序,要有順序,要有順序,若是遇到一些API,調試一直報錯,但又沒有特別的緣由,請注意看是不是順序問題。
內容 | 測試環境 | 生產環境 |
---|---|---|
api請求地址 | apitest url | api url |
Accept.js | Accept jstest url | Accept js url |
請求支付表單 | test payment/payment | accept payment/payment |
Manage Profiles | Manage Profiles | Manage Profiles |
Add Payment Profile | Add Payment Profile | Add Payment Profile |
Add Shipping Profile | Add Shipping Profile | Add Shipping Profile |
Edit Payment Profile | Edit Payment Profile | Edit Payment Profile |
Edit Shipping Profile | Edit Shipping Profile | Edit Shipping Profile |
須要請求的API : createCustomerProfileRequest
API的詳細文檔地址:createCustomerProfileRequestCustomerProfile
詳細介紹:customer_profiles
該API能夠在建立CustomerProfileId
的同時,也建立PaymentProfileId
。可是PaymentProfileId
須要的參數都是涉及到用戶敏感信息的,按照PCI標準,是不容許商戶收集,因此須要使用Authorize.net的託管表單來建立。
因此這一步只簡單的傳遞幾個參數便可,使用SDK建立代碼:
$customerProfile = new AnetAPI\CustomerProfileType(); $customerProfile->setDescription("Customer 2 Test PHP"); $customerProfile->setMerchantCustomerId('11211'); $customerProfile->setEmail($post['email']); $request = new AnetAPI\CreateCustomerProfileRequest(); $request->setMerchantAuthentication($this->merchantAuthentication); $request->setProfile($customerProfile); $controller = new AnetController\CreateCustomerProfileController($request); $response = $controller->executeWithApiResponse(\net\authorize\api\constants\ANetEnvironment::SANDBOX);
須要請求的API : getHostedProfilePageRequest
API的詳細文檔地址:getHostedProfilePageRequest
用上一步建立的CustomerProfileId
$profileId = $response->getCustomerProfileId();
來獲取token
$setting = new AnetAPI\SettingType(); $setting->setSettingName("hostedProfileIFrameCommunicatorUrl"); $url = \Yii::$app->urlManager->createAbsoluteUrl(['authorizenet/special']); $setting->setSettingValue($url); $request = new AnetAPI\GetHostedProfilePageRequest(); $request->setMerchantAuthentication($this->merchantAuthentication); $request->setCustomerProfileId($profileId); $request->addToHostedProfileSettings($setting); $controller = new AnetController\GetHostedProfilePageController($request); $response = $controller->executeWithApiResponse( \net\authorize\api\constants\ANetEnvironment::SANDBOX);
iframe
使用token
加載託管表單<form method="post" action="https://test.authorize.net/customer/addPayment" target="add_payment"> <input type="hidden" name="token" value="<?php echo $token;?>"/> <input id='submit' type="submit" value="添加支付信息"/> </form> <iframe id="add_payment" class="embed-responsive-item panel" name="add_payment" width="100%" height="650px" frameborder="0" scrolling="no"> </iframe>
此時該iframe
裏面尚未任何東西,須要提交這個form
表單才能加載託管表單,這裏給一個函數讓他頁面加載的時候自動提交以加載託管表單。
var button = document.getElementById('submit'); button.click();
咱們回到 1.2 申請表單這裏,這個API支持設置託管表單的不少屬性,比較有用的有 :
hostedProfileReturnUrl
: 設置託管會話結束(用戶點擊SAVE
)返回給用戶的頁面 (這裏省略)hostedProfileIFrameCommunicatorUrl
: 用來接受、處理Authorize.net響應的頁面
上面設置的hostedProfileIFrameCommunicatorUrl
的頁面爲authorizenet/special
function callParentFunction(str) { var referrer = document.referrer; var s = {qstr : str , parent : referrer}; if(referrer == 'https://test.authorize.net/customer/addPayment'){ switch(str){ case 'action=successfulSave' : window.parent.parent.location.href="https://www.basic.com/authorizenet/payment"; break; } } } function receiveMessage(event) { if (event && event.data) { callParentFunction(event.data); } } if (window.addEventListener) { window.addEventListener("message", receiveMessage, false); } else if (window.attachEvent) { window.attachEvent("onmessage", receiveMessage); } if (window.location.hash && window.location.hash.length > 1) { callParentFunction(window.location.hash.substring(1)); }
這裏設置成功保存paymentInfo
信息到Authorize.net以後就跳轉到 payment
頁面支付。action
有不一樣的狀態,能夠根據action做相應的處理。resizeWindow
: 託管表單加載successfulSave
: 表單成功保存(CustomerProfile)cancel
: 用戶點擊取消按鈕transactResponse
:支付成功(payment)
iframe
託管表單發起支付CustomerProfileId
,獲取用戶填寫的PaymentInfo
,用來回填支付表單須要請求的API : getCustomerProfileRequest
API的詳細文檔地址:getCustomerProfileRequest
$customer = $this->getCustomerProfile($profileId); $billTo = end($customer->getProfile()->getPaymentProfiles())->getBillTo();
由於一個CustomerProfi
對應多個PaymentProfile
,這裏獲取最後一個PaymentProfile
。
須要請求的API : getHostedPaymentPageRequest
API的詳細文檔地址:getHostedPaymentPageRequest
請求該URL,能夠指定加載表單的樣式等各類參數,具體參考:Accept Hosted feature details page
$transactionRequestType = new AnetAPI\TransactionRequestType(); $transactionRequestType->setTransactionType("authCaptureTransaction"); $transactionRequestType->setAmount("12.23"); $customer = $this->getCustomerProfile(\Yii::$app->session->get('profileId')); $billTo = end($customer->getProfile()->getPaymentProfiles())->getBillTo(); $transactionRequestType->setBillTo($billTo);//回填帳單地址 $customer = new AnetAPI\CustomerDataType(); $customer->setEmail(\Yii::$app->session->get('email')); $customer->setId(\Yii::$app->session->get('user_id')); $transactionRequestType->setCustomer($customer); $request = new AnetAPI\GetHostedPaymentPageRequest(); $request->setMerchantAuthentication($this->merchantAuthentication); $request->setTransactionRequest($transactionRequestType); $setting3 = new AnetAPI\SettingType(); $setting3->setSettingName("hostedPaymentReturnOptions"); $setting3->setSettingValue("{\"url\": \"https://www.basic.com/index.php?r=authorizenet/receipt\", \"cancelUrl\": \"https://www.basic.com/index.php?r=authorizenet/cancel\", \"showReceipt\": false}"); $request->addToHostedPaymentSettings($setting3); //設置託管表單顯示email,且必填 (由於form表單沒有禁止修改email參數,因此能夠設置email但不顯示在表單中,以防修改) $setting4 = new AnetAPI\SettingType(); $setting4->setSettingName('hostedPaymentCustomerOptions'); $setting4->setSettingValue("{\"showEmail\": true, \"requiredEmail\":true}"); $request->addToHostedPaymentSettings($setting4); $setting6 = new AnetAPI\SettingType(); $setting6->setSettingName('hostedPaymentIFrameCommunicatorUrl'); $url = \Yii::$app->urlManager->createAbsoluteUrl(['authorizenet/special']); $setting6->setSettingValue("{\"url\": \"".$url."\"}"); $request->addToHostedPaymentSettings($setting6); $controller = new AnetController\GetHostedPaymentPageController($request); $response = $controller->executeWithApiResponse( \net\authorize\api\constants\ANetEnvironment::SANDBOX); if (($response != null) && ($response->getMessages()->getResultCode() == "Ok") ) { return $response->getToken(); }
<body onload="func()"> <form id="send_hptoken" action="https://test.authorize.net/payment/payment" method="post" target="load_payment" > <input type="hidden" name="token" value="<?php echo $token ?>" /> <button type="submit" id="submit">我要支付</button> </form> <iframe id="load_payment" class="embed-responsive-item" name="load_payment" width="100%" height="650px" frameborder="0" scrolling="no"> </iframe> </body> <script type="application/javascript"> function func(){ var button = document.getElementById('submit'); button.click(); } </script>
同 二.1.14 一致,能夠設置爲同一個頁面,經過referrer
來判斷是完善支付信息表單的響應,仍是支付表單的響應
如:
if(referrer == 'https://test.authorize.net/customer/addPayment'){ //your code }else if(referrer == 'https://test.authorize.net/payment/payment'){ //your code }else if(other){ //your code }
(支付完成後的處理我沒作,無非就是彈個窗之類的告訴用戶支付成功,再處理後臺邏輯之類的)
能夠看到,這裏只能夠回填帳單地址、客戶電話和email之類的信息。信用卡、信用卡過時時間、信用卡安全碼等都沒法回填,須要用戶再次輸入,用戶體驗很是很差。
因此支付這一步咱們能夠不用託管表單,使用經過CustomerProfileID發起支付的API來完成
須要請求的API : createTransactionRequest
API的詳細文檔地址:createTransactionRequest
$paymentprofileid = $this->getCustomerProfile($profileid); $profileToCharge = new AnetAPI\CustomerProfilePaymentType(); $profileToCharge->setCustomerProfileId($profileid); $paymentProfile = new AnetAPI\PaymentProfileType(); $paymentProfile->setPaymentProfileId($paymentprofileid); $profileToCharge->setPaymentProfile($paymentProfile); $transactionRequestType = new AnetAPI\TransactionRequestType(); $transactionRequestType->setTransactionType( "authCaptureTransaction"); $transactionRequestType->setAmount(5); $transactionRequestType->setProfile($profileToCharge); $request = new AnetAPI\CreateTransactionRequest(); $request->setMerchantAuthentication($this->merchantAuthentication); $request->setTransactionRequest( $transactionRequestType); $controller = new AnetController\CreateTransactionController($request); $response = $controller->executeWithApiResponse( \net\authorize\api\constants\ANetEnvironment::SANDBOX);
託管表單要求你的程序掛載在HTTPS
域名下
還能夠經過CustomerProfileId、paymentProfileId發起ARB(Auto Recurring Billing)扣款
須要請求的API : ARBCreateSubscriptionRequest
API的詳細文檔地址:getHostedPaymentPageRequest
關於APB的詳細介紹請看:recurring_billing
關於測試請看:testing_guide
能夠填寫不一樣的 Zip Code 和 Card Code 來模擬不一樣的錯誤返回
(缺)
(缺)
(缺)
缺失的內容請自行參考官方demo。。。。。