From 0e94f51fa5cd66e19d66da89c7373925d8dd6a44 Mon Sep 17 00:00:00 2001 From: SangminLee Date: Wed, 28 Oct 2020 15:17:19 -0400 Subject: [PATCH] Add draft SDK for v2 --- README.md | 51 ++- README_v1.md | 185 ++++++++ UPGRADE.md | 31 ++ samples/api.php | 20 +- samples/callback.php | 27 +- samples/common.php | 12 + samples/index.html.php | 60 ++- samples/js/components/TpCollapse.vue | 5 +- samples/js/components/TpPayment.vue | 29 +- samples/js/components/TpSdkCredentials.vue | 50 ++- samples/js/components/TpSdkTransactions.vue | 12 +- samples/payment.php | 18 +- src/Models/Payment.php | 63 ++- src/Models/Transaction.php | 41 +- src/Models/VerifyTransactionRequest.php | 39 +- src/Models/VerifyTransactionResponse.php | 17 +- src/Traits/TendoPayHelper.php | 25 ++ src/Traits/TpHttpHelper.php | 146 ++++++ src/V2/ConstantsV2.php | 221 ++++++++++ src/V2/TendoPayClient.php | 465 ++++++++++++++++++++ 20 files changed, 1404 insertions(+), 113 deletions(-) create mode 100644 README_v1.md create mode 100644 UPGRADE.md create mode 100644 src/Traits/TpHttpHelper.php create mode 100644 src/V2/ConstantsV2.php create mode 100644 src/V2/TendoPayClient.php diff --git a/README.md b/README.md index 65b4d28..da584e1 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,15 @@ -# TendoPay SDK for PHP +# TendoPay SDK for PHP (v2) + +If you find a document for v1, please go to [TendoPay SDK for PHP (v1)](./README_v1.md) ## Requirements PHP 7.0 and later. +## Upgrade + +[UPGRADE from v1](./UPGRADE.md) + ## Installation ### Using Composer @@ -34,22 +40,15 @@ http://localhost:8000/ > MERCHANT_ID,MERCHANT_SECRET for test can get them at [TendoPay Sandbox](https://sandbox.tendopay.ph) ```bash -## Merchant Credentials -MERCHANT_ID= -MERCHANT_SECRET= - ## Client Credentials CLIENT_ID= CLIENT_SECRET= -## Enable Sandbox, it must be false in production -TENDOPAY_SANDBOX_ENABLED=false - ## Redirect URI when the transaction succeed REDIRECT_URL=https://localhost:8000/purhase.php -## Redirect URI when the transaction fails -ERROR_REDIRECT_URL=https://localhost:8000/purchase.php +## Enable Sandbox, it must be false in production +TENDOPAY_SANDBOX_ENABLED=false ``` ```php @@ -64,12 +63,9 @@ $client = new TendoPayClient(); use TendoPay\SDK\TendoPayClient; $config = [ - 'MERCHANT_ID' => '', - 'MERCHANT_SECRET' => '', 'CLIENT_ID' => '', 'CLIENT_SECRET' => '', 'REDIRECT_URL' => '', - 'ERROR_REDIRECT_URL' => '', 'TENDOPAY_SANDBOX_ENABLED' => false, ]; $client = new TendoPayClient($config); @@ -81,13 +77,13 @@ $client = new TendoPayClient($config); ```php use TendoPay\SDK\Exception\TendoPayConnectionException; use TendoPay\SDK\Models\Payment; -use TendoPay\SDK\TendoPayClient; +use TendoPay\SDK\V2\TendoPayClient; ### S:Merchant set proper values $merchant_order_id = $_POST['tp_merchant_order_id']; $request_order_amount = $_POST['tp_amount']; $request_order_title = $_POST['tp_description']; -$_SESSION['merchant_order_id'] = $merchant_order_id; +$redirectUrl = $_POST['tp_redirect_url'] ?? ''; ### E:Merchant set proper values $client = new TendoPayClient(); @@ -96,7 +92,10 @@ try { $payment = new Payment(); $payment->setMerchantOrderId($merchant_order_id) ->setDescription($request_order_title) - ->setRequestAmount($request_order_amount); + ->setRequestAmount($request_order_amount) + ->setCurrency('PHP') + ->setRedirectUrl($redirectUrl); + $client->setPayment($payment); @@ -114,21 +113,27 @@ try { ```php use TendoPay\SDK\Exception\TendoPayConnectionException; use TendoPay\SDK\Models\VerifyTransactionRequest; -use TendoPay\SDK\TendoPayClient; +use TendoPay\SDK\V2\TendoPayClient; $client = new TendoPayClient(); try { if (TendoPayClient::isCallBackRequest($_REQUEST)) { - $merchant_order_id = $_SESSION['merchant_order_id'] ?? null; - $transaction = $client->verifyTransaction($merchant_order_id, new VerifyTransactionRequest($_REQUEST)); + $transaction = $client->verifyTransaction(new VerifyTransactionRequest($_REQUEST)); if (!$transaction->isVerified()) { throw new UnexpectedValueException('Invalid signature for the verification'); } - // Save $transactionNumber here - // Proceed merchant post order process + if ($transaction->getStatus() == \TendoPay\SDK\V2\ConstantsV2::STATUS_SUCCESS) { + // PAID + // Save $transactionNumber here + // Proceed merchant post order process + } else if ($transaction->getStatus() == \TendoPay\SDK\V2\ConstantsV2::STATUS_FAILURE) { + // FAILED + // do something in failure case + // error message $transaction->getMessage() + } } } catch (TendoPayConnectionException $e) { echo 'Connection Error:'.$e->getMessage(); @@ -141,7 +146,7 @@ try { ```php use TendoPay\SDK\Exception\TendoPayConnectionException; -use TendoPay\SDK\TendoPayClient; +use TendoPay\SDK\V2\TendoPayClient; $client = new TendoPayClient(); @@ -161,7 +166,7 @@ try { ```php use TendoPay\SDK\Exception\TendoPayConnectionException; -use TendoPay\SDK\TendoPayClient; +use TendoPay\SDK\V2\TendoPayClient; $client = new TendoPayClient(); diff --git a/README_v1.md b/README_v1.md new file mode 100644 index 0000000..65b4d28 --- /dev/null +++ b/README_v1.md @@ -0,0 +1,185 @@ +# TendoPay SDK for PHP + +## Requirements + +PHP 7.0 and later. + +## Installation + +### Using Composer + +You can install the sdk via [Composer](http://getcomposer.org/). Run the following command: + +```bash +composer require tendopay/tendopay-sdk-php +``` + +## Run SDK Tester + +- Run a sample server +```bash +php -s localhost:8000 -t vendor/tendopay/tendopay-sdk-php/samples +``` + +- Open browser and goto +```bash +http://localhost:8000/ +``` + +## Code Examples + +### Create TendoPayClient + +- Using .env + > MERCHANT_ID,MERCHANT_SECRET for test can get them at [TendoPay Sandbox](https://sandbox.tendopay.ph) + +```bash +## Merchant Credentials +MERCHANT_ID= +MERCHANT_SECRET= + +## Client Credentials +CLIENT_ID= +CLIENT_SECRET= + +## Enable Sandbox, it must be false in production +TENDOPAY_SANDBOX_ENABLED=false + +## Redirect URI when the transaction succeed +REDIRECT_URL=https://localhost:8000/purhase.php + +## Redirect URI when the transaction fails +ERROR_REDIRECT_URL=https://localhost:8000/purchase.php +``` + +```php +use TendoPay\SDK\TendoPayClient; + +$client = new TendoPayClient(); +``` + +- Using $config variable + +```php +use TendoPay\SDK\TendoPayClient; + +$config = [ + 'MERCHANT_ID' => '', + 'MERCHANT_SECRET' => '', + 'CLIENT_ID' => '', + 'CLIENT_SECRET' => '', + 'REDIRECT_URL' => '', + 'ERROR_REDIRECT_URL' => '', + 'TENDOPAY_SANDBOX_ENABLED' => false, +]; +$client = new TendoPayClient($config); +``` + + +### Make Payment + +```php +use TendoPay\SDK\Exception\TendoPayConnectionException; +use TendoPay\SDK\Models\Payment; +use TendoPay\SDK\TendoPayClient; + +### S:Merchant set proper values +$merchant_order_id = $_POST['tp_merchant_order_id']; +$request_order_amount = $_POST['tp_amount']; +$request_order_title = $_POST['tp_description']; +$_SESSION['merchant_order_id'] = $merchant_order_id; +### E:Merchant set proper values + +$client = new TendoPayClient(); + +try { + $payment = new Payment(); + $payment->setMerchantOrderId($merchant_order_id) + ->setDescription($request_order_title) + ->setRequestAmount($request_order_amount); + + $client->setPayment($payment); + + $redirectURL = $client->getAuthorizeLink(); + header('Location: '.$redirectURL); +} catch (TendoPayConnectionException $e) { + echo 'Connection Error:'.$e->getMessage(); +} catch (Exception $e) { + echo 'Runtime Error:'.$e->getMessage(); +} +``` + +### Callback (redirected page) + +```php +use TendoPay\SDK\Exception\TendoPayConnectionException; +use TendoPay\SDK\Models\VerifyTransactionRequest; +use TendoPay\SDK\TendoPayClient; + +$client = new TendoPayClient(); + +try { + if (TendoPayClient::isCallBackRequest($_REQUEST)) { + $merchant_order_id = $_SESSION['merchant_order_id'] ?? null; + $transaction = $client->verifyTransaction($merchant_order_id, new VerifyTransactionRequest($_REQUEST)); + + if (!$transaction->isVerified()) { + throw new UnexpectedValueException('Invalid signature for the verification'); + } + + // Save $transactionNumber here + // Proceed merchant post order process + } +} catch (TendoPayConnectionException $e) { + echo 'Connection Error:'.$e->getMessage(); +} catch (Exception $e) { + echo 'Runtime Error:'.$e->getMessage(); +} +``` + +### Cancel Payment + +```php +use TendoPay\SDK\Exception\TendoPayConnectionException; +use TendoPay\SDK\TendoPayClient; + +$client = new TendoPayClient(); + +try { + $client->cancelPayment($transactionNumber); + // merchant process here + +} catch (TendoPayConnectionException $e) { + echo 'Connection Error:'.$e->getMessage(); +} catch (Exception $e) { + echo 'Runtime Error:'.$e->getMessage(); +} +``` + + +### Show Transaction Detail + +```php +use TendoPay\SDK\Exception\TendoPayConnectionException; +use TendoPay\SDK\TendoPayClient; + +$client = new TendoPayClient(); + +try { + + $transaction = $client->getTransactionDetail($transactionNumber); + + // merchant process here + // $transaction->getMerchantId(); + // $transaction->getMerchantOrderId(); + // $transaction->getAmount(); + // $transaction->getTransactionNumber(); + // $transaction->getCreatedAt(); + // $transaction->getStatus(); + +} catch (TendoPayConnectionException $e) { + echo 'Connection Error:'.$e->getMessage(); +} catch (Exception $e) { + echo 'Runtime Error:'.$e->getMessage(); +} +``` diff --git a/UPGRADE.md b/UPGRADE.md new file mode 100644 index 0000000..a707ea5 --- /dev/null +++ b/UPGRADE.md @@ -0,0 +1,31 @@ +# Upgrade Guide + +## # Upgrading to v2 from 0.8.x + +### Changes + +##### New Client ID/ Client Secret + Merchant should create a new Client App(REST V2) in the Merchant Dashboard to use SDK 2.x + Existing credentils is not compatible with SDK 2.x + +##### MERCHANT_ID/MERCHANT_SECRET + MERCHANT_ID/MERCHANT_SECRET has been removed + +##### ERROR_REDIRECT_URL + ERROR_REDIRECT_URL has been removed. + All successful or unsuccessful response will be redirected to REDIRECT_URL + +##### Backend Notification + When a merchant creates an app (REST V2), + if 'NOTIFICATION_URL' is set, TendoPay notifies some changes of transactions to the notification callback URL. + This callback is asynchronous back-end API request. + 'PAID', 'FAILED', 'CANCELLED' events of transactions are triggered. + +##### Change the namespace of TendoPayClient +```php + # TendoPayClient for v1 + use TendoPay\SDK\TendoPayClient; + + # TendoPayClient for v2 + use TendoPay\SDK\V2\TendoPayClient; +```` diff --git a/samples/api.php b/samples/api.php index 933e957..7a67692 100644 --- a/samples/api.php +++ b/samples/api.php @@ -2,20 +2,8 @@ require_once __DIR__.'/common.php'; -use TendoPay\SDK\TendoPayClient; global $config; -/** - * - * @param mixed $response - * @param int $status - */ -function json($response = [], $status = 200) -{ - echo header('Content-Type: application/json', true, $status); - echo json_encode($response); -} - ## Main $request = json_decode(file_get_contents('php://input'), false); $job = $request->job ?? null; @@ -31,13 +19,17 @@ function json($response = [], $status = 200) case 'GET_TRANSACTIONS': return json($_SESSION['transactions'] ?? []); case 'GET_TRANSACTION': - $client = new TendoPayClient($config); + $client = ($config['TP_SDK_VERSION'] ?? null) === 'v2' ? + new \TendoPay\SDK\V2\TendoPayClient($config) : + new \TendoPay\SDK\TendoPayClient($config); $response = $client->getTransactionDetail($request->transactionNumber); return json($response->toArray()); case 'CANCEL_TRANSACTION': $transactionNumber = $request->transactionNumber; - $client = new TendoPayClient($config); + $client = ($config['TP_SDK_VERSION'] ?? null) === 'v2' ? + new \TendoPay\SDK\V2\TendoPayClient($config) : + new \TendoPay\SDK\TendoPayClient($config); $client->cancelPayment($transactionNumber); $_SESSION['transactions'] = array_filter($_SESSION['transactions'] ?? [], diff --git a/samples/callback.php b/samples/callback.php index 98ce8fa..7cb4f51 100644 --- a/samples/callback.php +++ b/samples/callback.php @@ -4,13 +4,32 @@ use TendoPay\SDK\Exception\TendoPayConnectionException; use TendoPay\SDK\Models\VerifyTransactionRequest; -use TendoPay\SDK\TendoPayClient; global $config; -$client = new TendoPayClient($config); +//echo "
";print_r($_REQUEST);echo "
";exit; +$client = $config['TP_SDK_VERSION'] === 'v2' ? + new \TendoPay\SDK\V2\TendoPayClient($config) : + new \TendoPay\SDK\TendoPayClient($config); try { - if (TendoPayClient::isCallBackRequest($_REQUEST)) { + if ($config['TP_SDK_VERSION'] === 'v2' && + \TendoPay\SDK\V2\TendoPayClient::isCallBackRequest($_REQUEST)) { + + $transaction = $client->verifyTransaction(new VerifyTransactionRequest($_REQUEST)); + + if (!$transaction->isVerified()) { + throw new UnexpectedValueException('Invalid signature for the verification'); + } + +// dump('verificationResult:', $transaction->toArray());exit; + // @Note check request amount with approved amount. it can be different but must be enough to purchase + // Save $transactionNumber here + $transactions = $_SESSION['transactions'] ?? []; + $transactions[] = $transaction->toArray(); + $_SESSION['transactions'] = $transactions; + header('Location: /'); + + } elseif (\TendoPay\SDK\TendoPayClient::isCallBackRequest($_REQUEST)) { $merchant_order_id = $_SESSION['merchant_order_id'] ?? null; $transaction = $client->verifyTransaction($merchant_order_id, new VerifyTransactionRequest($_REQUEST)); @@ -18,7 +37,7 @@ throw new UnexpectedValueException('Invalid signature for the verification'); } -// dump('verificationResult:', $transaction->toArray()); +// dump('verificationResult:', $transaction->toArray());exit; // @Note check request amount with approved amount. it can be different but must be enough to purchase // Save $transactionNumber here $transactions = $_SESSION['transactions'] ?? []; diff --git a/samples/common.php b/samples/common.php index 6dabd8f..0624ac6 100644 --- a/samples/common.php +++ b/samples/common.php @@ -25,6 +25,17 @@ function dump() echo ''; } +/** + * + * @param mixed $response + * @param int $status + */ +function json($response = [], $status = 200) +{ + echo header('Content-Type: application/json', true, $status); + echo json_encode($response); +} + $credentials = $_SESSION['credentials'] ?? null; //putenv('MERCHANT_ID='.$credentials->merchant_id); //putenv('MERCHANT_SECRET='.$credentials->merchant_secret); @@ -35,6 +46,7 @@ function dump() //putenv('TENDOPAY_SANDBOX_ENABLED=true'); $config = [ + 'TP_SDK_VERSION' => $credentials->tp_sdk_version ?? '', 'MERCHANT_ID' => $credentials->merchant_id ?? '', 'MERCHANT_SECRET' => $credentials->merchant_secret ?? '', 'CLIENT_ID' => $credentials->client_id ?? '', diff --git a/samples/index.html.php b/samples/index.html.php index 5019ba4..1d49986 100644 --- a/samples/index.html.php +++ b/samples/index.html.php @@ -39,10 +39,10 @@

- +

- +

@@ -70,6 +70,7 @@ }, data: { credentials: { + tp_sdk_version: 'v2', merchant_id: null, merchant_secret: null, client_id: null, @@ -85,6 +86,28 @@ transactions: null, }, methods: { + initOrder () { + const tp_merchant_order_id = `TEST-ORD-${Date.now()}` + this.payload = { + tp_merchant_order_id, + tp_description: `Invoice #${tp_merchant_order_id}`, + tp_amount: 1500 + } + }, + async saveCredentials () { + try { + window.setTimeout(async () => { + const { data } = await axios.post(API_END_POINT, { + job: 'SAVE_CREDENTIALS', + credentials: this.credentials + }) + // console.log('saveCredentials', data) + this.$emit('input', this.credentials) + }, 400) + } catch (e) { + console.log('error:saveCredentials() ', e) + } + }, async submit () { // console.log('submit', this.payload) const { data } = await axios.post(API_END_POINT, { @@ -92,12 +115,41 @@ }) // console.log('getCredentials', data) }, - async initTransactions() { + async initTransactions () { const { data } = await axios.post(API_END_POINT, { job: 'GET_TRANSACTIONS' }) // console.log('getTransactions', data) this.transactions = data + }, + async submitForm () { + console.log('submit form:', this.credentials, this.payload) + const { tp_sdk_version = '', redirect_url = '' } = this.credentials + const params = { + ...this.payload, + tp_sdk_version, + tp_redirect_url: redirect_url, + } + // const { data } = await axios.post('/payment.php', params) + // console.log('data', data) + + const form = document.createElement('form') + form.method = 'POST' + form.action = '/payment.php' + + for (const key in params) { + if (params.hasOwnProperty(key)) { + const hiddenField = document.createElement('input') + hiddenField.type = 'hidden' + hiddenField.name = key + hiddenField.value = params[key] + + form.appendChild(hiddenField) + } + } + + document.body.appendChild(form) + form.submit() } }, mounted () { @@ -105,6 +157,8 @@ ...this.credentials, ...initCredentials, } + this.initOrder() + this.saveCredentials() this.initTransactions() } }) diff --git a/samples/js/components/TpCollapse.vue b/samples/js/components/TpCollapse.vue index b3a1804..ab89ade 100644 --- a/samples/js/components/TpCollapse.vue +++ b/samples/js/components/TpCollapse.vue @@ -1,13 +1,14 @@