こんにちは。sohnishiです。
PayPay for Developersを使用してPayPay決済を実装する際に、
決済の流れや決済完了後に処理を行いたい場合の流れなど、
私の実現したかった内容が纏まっている文献がなかったため、備忘録として残しておきます。
処理の流れ
- フロントエンド(モバイルアプリ)からサーバーサイドにPOSTリクエストを送信。
- サーバーサイドで支払いページのURLを作成し、戻り値にセット。
- フロントエンドは受け取ったURLにアクセスし、ユーザーが支払いを実行。支払い完了後はディープリンクでアプリに戻る。
- サーバーサイドはWebhookで支払いデータを受け取る。
composer
1 |
composer require paypayopa/php-sdk |
ソースコード
.env
1 2 3 4 5 6 7 8 9 10 11 12 |
PAYPAY_API_KEY=〜〜 PAYPAY_API_SECRET=〜〜 PAYPAY_MERCHANT_ID=〜〜 PAYPAY_WEBHOOK_WHITELIST_ENABLE=true PAYPAY_WEBHOOK_WHITELIST_PROXY=false PAYPAY_WEBHOOK_WHITELIST_IP_ADDRESS_01=13.112.237.64 PAYPAY_WEBHOOK_WHITELIST_IP_ADDRESS_02=52.199.148.9 PAYPAY_WEBHOOK_WHITELIST_IP_ADDRESS_03=54.199.212.149 PAYPAY_WEBHOOK_WHITELIST_IP_ADDRESS_04=13.208.106.122 PAYPAY_WEBHOOK_WHITELIST_IP_ADDRESS_05=13.208.115.200 PAYPAY_WEBHOOK_WHITELIST_IP_ADDRESS_06=13.208.152.196 APP_DEEPLINK=https://〜〜 |
config/paypay.php
1 2 3 4 5 6 7 |
<?php return [ 'PAYPAY_API_KEY' => env('PAYPAY_API_KEY'), 'PAYPAY_API_SECRET' => env('PAYPAY_API_SECRET'), 'PAYPAY_MERCHANT_ID' => env('PAYPAY_MERCHANT_ID'), 'APP_DEEPLINK' => env('APP_DEEPLINK') ]; |
config/paypay-webhook-whitelist.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?php return [ 'enable' => env('PAYPAY_WEBHOOK_WHITELIST_ENABLE', false), 'isProxy' => env('PAYPAY_WEBHOOK_WHITELIST_PROXY', false), 'allowIps' => [ env('PAYPAY_WEBHOOK_WHITELIST_LOCALHOST_IP', '127.0.0.1'), env('PAYPAY_WEBHOOK_WHITELIST_IP_ADDRESS_01', false), env('PAYPAY_WEBHOOK_WHITELIST_IP_ADDRESS_02', false), env('PAYPAY_WEBHOOK_WHITELIST_IP_ADDRESS_03', false), env('PAYPAY_WEBHOOK_WHITELIST_IP_ADDRESS_04', false), env('PAYPAY_WEBHOOK_WHITELIST_IP_ADDRESS_05', false), env('PAYPAY_WEBHOOK_WHITELIST_IP_ADDRESS_06', false), ], ]; |
Middleware/PaypayWebhookWhitelist.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Support\Facades\Log; class PaypayWebhookWhitelist { /** * Handle an incoming request. * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { $config = \Config::get('paypay-webhook-whitelist'); if ($config['enable'] !== true) { return $next($request); } if ($config['isProxy'] === true) { $request->setTrustedProxies([$request->ip()]); } if ($this->isAllow($request->ip(), $config['allowIps']) === false) { $error_message = 'cannot access from : ' . $request->ip(); Log::debug($error_message); abort(404, $error_message); } return $next($request); } private function isAllow(string $remoteIp, array $accepts) { foreach ($accepts as $accept) { if ($this->isIn($remoteIp, $accept)) { return true; } } return false; } private function isIn(string $remoteIp, string $accept) { if (strpos($accept, '/') === false) { return ($remoteIp === $accept); } list($acceptIp, $mask) = explode('/', $accept); $acceptLong = ip2long($acceptIp) >> (32 - (int) $mask); $remoteLong = ip2long($remoteIp) >> (32 - (int) $mask); return ($acceptLong == $remoteLong); } } |
Http/Kernel.php
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?php namespace App\Http; use Illuminate\Foundation\Http\Kernel as HttpKernel; class Kernel extends HttpKernel { protected $routeMiddleware = [ // 関係のない部分は省略しています。 'paypayWebhookWhitelist' => \App\Http\Middleware\PaypayWebhookWhitelist::class, ]; } |
API_KEYやAPI_SECRET等のAPIに必要な情報をPayPay for Developersで取得し、
.envに記載してください。
また、WebhookにアクセスできるIPを制限するため、PayPay側のIPアドレスも記載します。
IPアドレスは念のため最新のものを確認してください。
PayPay決済完了後にアプリに戻るため、ディープリンクも記載してください。
api.php
1 2 3 4 |
Route::post('paypay/qrcode/create', [\App\Http\Controllers\PaypayController::class, 'createQrCode']); Route::group(['middleware' => 'paypayWebhookWhitelist'], function (Router $router) { $router->post('wh/paypay', [\App\Http\Controllers\PaypayWebhookController::class, 'handlePaypayWebhook']); }); |
ルーティングの定義を行います。
PaypayController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Exception; use PayPay\OpenPaymentAPI\Client; use PayPay\OpenPaymentAPI\Models\OrderItem; use PayPay\OpenPaymentAPI\Models\CreateQrCodePayload; class PaypayController extends Controller { public function createQrCode(Request $request) { try { $client = new Client([ 'API_KEY' => config('paypay.PAYPAY_API_KEY'), 'API_SECRET'=> config('paypay.PAYPAY_API_SECRET'), 'MERCHANT_ID'=> config('paypay.PAYPAY_MERCHANT_ID') ], config('app.env') == 'production' ? true : false); $items = (new OrderItem()) ->setName('hogehoge') // 識別できる値を任意でセットしてください。 ->setQuantity(1) // 個数 ->setUnitPrice(['amount' => 200, 'currency' => 'JPY']); // 値段と通貨 $payload = new CreateQrCodePayload(); $payload->setOrderItems($items); $merchantPaymentId = "mpid_".rand().time(); $payload->setMerchantPaymentId($merchantPaymentId); $payload->setCodeType("ORDER_QR"); $payload->setAmount(["amount" => 200, "currency" => "JPY"]); $payload->setRedirectType('APP_DEEP_LINK'); $payload->setIsAuthorization(false); $payload->setRedirectUrl(config('paypay.APP_DEEPLINK')); $payload->setUserAgent($_SERVER['HTTP_USER_AGENT']); $QRCodeResponse = $client->code->createQRCode($payload); if($QRCodeResponse['resultInfo']['code'] !== 'SUCCESS') { return response()->json([ 'message' => 'QRコード生成エラー', ], 500, [], JSON_UNESCAPED_UNICODE); } return [ 'url' => $QRCodeResponse['data']['url'] ]; } catch (Exception $e) { \Log::error($e->getMessage()); return response()->json([ 'message' => $e->getMessage(), ], 500, [], JSON_UNESCAPED_UNICODE); } } } |
商品情報を定義し、QRコード(支払いページのURL)を作成します。
作成したURLをレスポンスで返却します。
PaypayWebhookController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; class PaypayWebhookController extends Controller { public function handlePaypayWebhook(Request $request) { try { if ($request->state == 'COMPLETED') { // 決済完了後の処理 } return response()->json([ 'result' => true, ], 200, [], JSON_UNESCAPED_UNICODE); } catch (\Exception $e) { \Log::error($e->getMessage()); return response()->json([ 'message' => $e->getMessage(), ], 500, [], JSON_UNESCAPED_UNICODE); } } } |
Webhookで支払い完了データを受けとった際の処理を定義します。
あとはアプリ側からpaypay/qrcode/create
にPOSTリクエストを送信し、戻り値でURLを受け取ってください。
受け取ったURLにアクセスすると、支払いページが表示されます。
Webhookのエンドポイントの設定等はPayPay for Developersから問い合わせることで可能です。
以上、PayPay決済実装方法のご紹介でした。
万が一、間違っている箇所等ありましたらコメントにてご指摘いただけますと幸いです!
参考にさせていただいた記事
https://kawaidesu.hatenablog.com/entry/2020/07/31/135428