前言
公司一個網站項目有國外的用戶給我們發郵件希望能用paypal支付,於是交給了我,我們這個項目兩年前是有對接paypal通道的,但是一直沒有開放,測試測了並不能完成付款流程。
看paypal官方是最近有出一個新的sdk,老的應該不在支持更新了,於是打算用新的SDK重新對接,新的github地址:https://github.com/paypal/Checkout-PHP-SDK/。
這個項目使用的是thinkphp5框架,但是其他框架使用方法也都差不多。
本文只是隨筆記錄,如果能幫到其他朋友最好,如果有哪里錯誤或者有疑問的可以在評論區指出,我看到會及時回復的!!!
准備
測試賬戶登錄https://developer.paypal.com開發者中心
點開並創建新的App,注意這里分sanbox沙盒環境和live環境,沙盒環境可以創建測試付款和收款賬戶,live是正式環境的。
左側account可以會有默認的商家賬戶和普通用戶,可以用來沙盒環境測試。
遇到的坑
沙盒環境下用戶能扣款,也能收到通知,但是商家賬戶沒有收款記錄,發郵件給paypal,說是他們幫我確認下收款郵箱了,然后再試就可以了。
正式環境下國內大陸的paypal賬戶不能給國內大陸的收款賬戶付款,但是用visa等信用卡可以選擇香港地區進行付款
對接流程
composer.json添加“paypal/Checkout-PHP-SDK”之后執行 composer update paypal/Checkout-PHP-SDK 記得要帶包名,不然所有的包全都更新到最新版了
更新好之后會發現vendor目錄多了兩個包
並且有寫好的demo
我們目前需要使用的是發起訂單,訂單扣款,以及訂單退款。
發起訂單
case 'paypal': if(!isset($Config['app_id']) || !isset($Config['app_secret'])){ throw new \think\Exception('paypal配置錯誤'); } $request = new OrdersCreateRequest(); $request->prefer('return=representation'); $returnUrl = isset($Config['return_url']) ? $Config['return_url'] : 'https://xxx.com/pay/callback/paypal?act=success'; //成功跳轉地址 $cancelUrl = isset($Config['cancel_url']) ? $Config['cancel_url'] : 'https://xxx.com/pay/callback/paypal?act=cancel'; //取消跳轉地址 $request->body = array( 'intent' => 'CAPTURE', 'application_context' => array( 'return_url' => $returnUrl, 'cancel_url' => $cancelUrl ), 'purchase_units' => array( 0 => array( 'amount' => array( 'currency_code' => 'USD', //幣種-paypal文檔有 'value' => '0.01' //金額 ) ) ) ); // 是否是沙盒模式 $envSet = $Config['use_sandbox'] ? 'PayPalCheckoutSdk\Core\SandboxEnvironment' : 'PayPalCheckoutSdk\Core\ProductionEnvironment'; try { $environment = new $envSet($Config['app_id'], $Config['app_secret']); $client = new PayPalHttpClient($environment); $response = $client->execute($request); $url = $response->result->links[1]->href; return $url } catch (HttpException $ex) { throw $ex; //echo $ex->statusCode; //print_r($ex->getMessage()); } break;
這里$Config變量是配置參數
這里return的url是需要前端跳轉的paypal收銀台,用戶確認支付后會跳轉(這里是同步跳轉,異步回調需要ipn或者webhook,后面會提到)
捕獲訂單
用戶支付完成跳轉到我們這里並沒有完成付款,只是給了授權而已,我們還要根據訂單的狀態來進行扣款操作。
跳轉回來或者異步回調會有“token“參數,可以根據token查詢當前的訂單狀態。
訂單狀態:
CREATED
. The order was created with the specified context. 創建訂單SAVED
. The order was saved and persisted. The order status continues to be in progress until a capture is made withfinal_capture = true
for all purchase units within the order.APPROVED
. The customer approved the payment through the PayPal wallet or another form of guest or unbranded payment. For example, a card, bank account, or so on. 已授權VOIDED
. All purchase units in the order are voided.COMPLETED
. The payment was authorized or the authorized payment was captured for the order.public function capture($token){ //1獲取token 2 OrdersGetRequest判斷token狀態status 3.COMPLETED 則已完成 APPROVED 則已經批准 進入 captureOrder 判斷返回結果COMPLETED 則已完成 其他則打印log try { //client $Config = \think\Config::get('paypal'); $envSet = $Config['use_sandbox'] ? 'PayPalCheckoutSdk\Core\SandboxEnvironment' : 'PayPalCheckoutSdk\Core\ProductionEnvironment'; $environment = new $envSet($Config['app_id'], $Config['app_secret']); $client = new PayPalHttpClient($environment); //先判斷訂單當前狀態-防止訂單已經是已完成狀態,再次扣款 $request = new OrdersGetRequest($token); $response = $client->execute($request); if(!isset($response->result->status)) throw new HttpException(__CLASS__ .'參數未獲取到,token:'.$token); $status = $response->result->status; switch ($status){ case 'COMPLETED': //訂單已經完成 // break; case 'APPROVED': //獲取支付金額 $captureRequest = new OrdersCaptureRequest($token); $captureRequest->prefer('return=minimal'); //簡潔參數-獲取更多參數用return=representation $captureResponse = $client->execute($captureRequest); //返回狀態不是已完成 if($captureResponse->result->status != 'COMPLETED') throw new HttpException(__CLASS__ .'captureOrder 失敗,token:'.$token.' status:'.$captureResponse->result->status); break; default: //其他 throw new HttpException(__CLASS__ .'參數未知,token:'.$token.' status:'.$status); break; } //訂單支付完成 return true; }catch (HttpException $ex) { Log::write($ex->getMessage()); return false; } }
訂單退款
$request = new CapturesRefundRequest($token); $Config = \think\Config::get('paypal'); $envSet = $Config['use_sandbox'] ? 'PayPalCheckoutSdk\Core\SandboxEnvironment' : 'PayPalCheckoutSdk\Core\ProductionEnvironment'; $environment = new $envSet($Config['app_id'], $Config['app_secret']); $client = new PayPalHttpClient($environment); $response = $client->execute($request);
退款流程應也是根據token來操作,但是我這邊提示我“description":"You do not have permission to access or perform operations on this resource.”
https://stackoverflow.com/questions/54907374/paypal-refund-rest-api-v2-authorization-failed-due-to-insufficient-permissions 這里說是需要新建一個app_id才行,因為我們這個項目退款要經過財務,所以就沒繼續測試下去,有走完流程的朋友可以說一下。
付款回調
paypal應該是ipn和webhook,但是我看Stack Overflow上有人說ipn方式可能會廢棄,所以我們采用了webhook方式
webhook需要點開對應的APP下去添加
這里可以選全部也可以選需要的事件