vgspedro/vivaapi

Viva Wallet 本地结账 V2 API

1 2020-07-21 15:05 UTC

This package is auto-updated.

Last update: 2024-09-05 07:17:57 UTC


README

本软件包基于Aleksey Kuleshov的工作。

已修改以满足我的需求。

这是Viva Wallet本地结账V2 API的包装器: https://developer.vivawallet.com/online-checkouts/native-checkout-v2/

如何使用

此库通过 Composer 安装。您需要要求 vgspedro/vivaapi

composer require vgspedro/vivaapi

Symfony框架

创建控制器

src/Controller/Payment.php

namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

use App\Service\VivaWallet;

class PaymentController extends AbstractController
{
    private $environment;
    private $amount = 36812;

    public function __construct(ParameterBagInterface $environment)
    {
        $this->environment = $environment;
        $this->amount = 36812;
    }

    public function index(VivaWallet $viva)
    {

        return $this->render('admin/payment/native.html', [
            'amount' => $this->amount,
            'viva_token' => $viva->getCardChargeToken(),
            'sf_v' => \Symfony\Component\HttpKernel\Kernel::VERSION,
            'payment_url' => $this->environment->get("kernel.environment") == 'prod' ? 'https://www.vivapayments.com' : 'https://demo-api.vivapayments.com',
        ]);
    }


    /**
    * Make some transations accordind the "action" value from the form
    **/
    public function submit(Request $request, VivaWallet $viva)
    {       
        $pre_auth = $request->request->get('action') == 'authorization' ? false : true;

        $client = [
            'email' => $request->request->get('email'),
            'phone' => $request->request->get('phone'),
            'full_name' => $request->request->get('name'),
            'request_lang' => 'pt',
            'country_code' => 'PT'
        ];

        $transaction = [
            'amount' => $this->amount,
            'installments' => 1,
            'charge_token' => $request->request->get('token'),
            'merchant_trans' => 'Information to the Merchant',
            'customer_trans' => 'Information to the Client ' .$request->request->get('action'),
            'tip_amount' => 0,
            'pre_auth' => $pre_auth,
            'currency_code' => 978// https://pt.iban.com/currency-codes
        ];

        if($request->request->get('action') == 'charge'){
            $charge = $viva->setCharge($client, $transaction);
            //Something went wrong send info to user
            if ($charge['status'] == 0)
                return new JsonResponse([
                    'status' => 0,
                    'message' => $charge['data'],
                    'data' => $charge
                ]);

            return new JsonResponse([
                'status' => 1,
                'message' => $charge['data'],
                'data' => $trans
            ]);

        }
        
        else if($request->request->get('action') == 'authorization'){    
            $charge = $viva->setAutorization($client, $transaction);
            //Something went wrong send info to user
            if ($charge['status'] == 0)
                return new JsonResponse([
                    'status' => 0,
                    'message' => $charge['data'],
                    'data' => $charge
                ]);
            
            return new JsonResponse([
                'status' => 1,
                'message' => $charge['data'],
                'data' => $charge
            ]);

        }

        else if($request->request->get('action') == 'charge_capture'){
            $charge = $viva->setCharge($client, $transaction);
            //Something went wrong send info to user
            if ($charge['status'] == 0)
                return new JsonResponse([
                    'status' => 0,
                    'message' => $charge['data'],
                    'data' => $charge
                ]);
            
            $capture = $viva->setCapture($charge['data']->transactionId, $transaction['amount']);
            
            return new JsonResponse([
                'status' => 1,
                'message' => $capture['data'],
                'data' => $capture
            ]);
        }

        else if($request->request->get('action') == 'charge_cancel'){
            $charge = $viva->setCharge($client, $transaction);

            //Something went wrong send info to user
            if ($charge['status'] == 0)
                return new JsonResponse([
                    'status' => 0,
                    'message' => $charge['data'],
                    'data' => $charge
                ]);
            
            //
            $cancel = $viva->setCancel($charge['data']->transactionId, $transaction['amount']);
            
            return new JsonResponse([
                'status' => 1,
                'message' => $cancel['data'],
                'data' => $cancel
            ]);

        }

        //Something went wrong send info to user
            return new JsonResponse([
                'status' => 0,
                'message' => 'Not Processed',
                'data' => null
            ]);
    }

}

创建服务

src/Service/VivaWallet.php

namespace App\Service;

use \VgsPedro\VivaApi\Transaction\Authorization;
use \VgsPedro\VivaApi\Transaction\Url;
use \VgsPedro\VivaApi\Transaction\Customer;
use \VgsPedro\VivaApi\Transaction\Charge;
use \VgsPedro\VivaApi\Transaction\Capture;
use \VgsPedro\VivaApi\Transaction\Cancel;
use \VgsPedro\VivaApi\Transaction\ChargeToken;
use \VgsPedro\VivaApi\Transaction\Installments;

use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;

class VivaWallet
{

	private $test_mode; // Boolean 
 	private $client_id; // Client ID, Provided by wallet
 	private $client_secret; // Client Secret, Provided by wallet
    private $url; // Url to make request, sandbox or live (sandbox APP_ENV=dev or test) (live APP_ENV=prod)
    private $merchant_id; //Merchant ID , Provided by wallet
    private $api_key; //Api Key, Provided by wallet
	private $headers; //Set the authorization to curl

    public function __construct(ParameterBagInterface $environment){
		$this->test_mode = true;
 		$this->client_id = 'ef7ee87mrt0grg62dbmwms0xzvu29owz5202f9b03ceo7.apps.vivapayments.com';
		$this->client_secret = '4M7ug3jfUh1wZ2Q442Y0L3MDxHz35E';
		$this->api_key = '71-}w%';
        $this->url = $environment->get("kernel.environment") == 'prod' ? 'https://www.vivapayments.com' : 'https://demo-api.vivapayments.com';
    }

	/**
	* Create an authentication Token to pass to client side js  
	* @return string $accessToken 
	**/
	public function getCardChargeToken(){

		$baseUrl = Url::getUrl($this->test_mode); //Test mode, default is false
		$accessToken = (new Authorization())
		->setClientId($this->client_id) // Client ID, Provided by wallet
		->setClientSecret($this->client_secret) // Client Secret, Provided by wallet
		->setTestMode($this->test_mode) // Test mode, default is false, can be skipped
		->getAccessToken();
		return $accessToken;
	}

	/**
	* Create a charge transaction
	*@param $client // Information of the user 
	*@param $trans // Information of the charge transaction 
	**/	
	public function setCharge(array $client, array $trans){

		$customer = (new Customer())
			->setEmail($client['email'])
			->setPhone($client['phone'])
			->setFullName($client['full_name'])
	      	->setRequestLang($client['request_lang'])
      		->setCountryCode($client['country_code']);

		$transaction = (new Charge())
			->setClientId($this->client_id) // Client ID, Provided by wallet
			->setClientSecret($this->client_secret) // Client Secret, Provided by wallet
			->setTestMode($this->test_mode) // Test mode, default is false, can be skipped
			->setSourceCode('') // Source code, provided by wallet
			->setAmount($trans['amount']) // The amount to charge in currency's smallest denomination (e.g amount in pounds x 100)
			->setInstallments($trans['installments']) // Installments, can be skipped if not used
			->setChargeToken($trans['charge_token']) // Charge token obtained at front end
 			->setMerchantTrns( $trans['merchant_trans'])
 			->setCustomerTrns($trans['customer_trans'])
			->setTipAmount($trans['tip_amount'])
			->setCustomer($customer)
			->setPreAuth($trans['pre_auth']); //If true, a PreAuth transaction will be performed. This will hold the selected amount as unavailable (without the customer being charged) for a period of time.

		$result = $transaction->send();

		if (!empty($transaction->getError()))
			return [
				'status' => 0,
				'data' => $transaction->getError()
			];
			
		return [
			'status' => 1,
			'data' => $result
		];
	}

	/**
	* Create a charge transaction, the amount is captured and charged. 
	*@param $client // Information of the user 
	*@param $trans // Information of the charge transaction 
	**/	
	public function setAutorization(array $client, array $trans){

		$customer = (new Customer())
			->setEmail($client['email'])
			->setPhone($client['phone'])
			->setFullName($client['full_name'])
	      	->setRequestLang($client['request_lang'])
      		->setCountryCode($client['country_code']);

		$transaction = (new Authorization())
			->setClientId($this->client_id) // Client ID, Provided by wallet
			->setClientSecret($this->client_secret) // Client Secret, Provided by wallet
			->setTestMode($this->test_mode) // Test mode, default is false, can be skipped
			->setSourceCode('') // Source code, provided by wallet
			->setAmount($trans['amount']) // The amount to pre-auth in currency's smallest denomination (e.g amount in pounds x 100)
			->setInstallments($trans['installments']) // Installments, can be skipped if not used
			->setChargeToken($trans['charge_token']) // Charge token obtained at front end
			->setCustomer($customer)
			->setPreAuth($trans['pre_auth']);//If true, a PreAuth transaction will be performed. This will hold the selected amount as unavailable (without the customer being charged) for a period of time.

		$result = $transaction->send();

		if (!empty($transaction->getError()))
			return [
				'status' => 0,
				'data' => $transaction->getError()
			];
			
		return [
			'status' => 1,
			'data' => $result
		];
	}


	/**
	* Capture a charge transaction
	*@param $t_i // Transaction id of authorization transaction
	*@param $amount // The amount to capture in currency's smallest denomination (e.g amount in pounds x 100)
	**/
	public function setCapture(string $t_i, int $amount){

		$transaction = (new Capture())
			->setClientId($this->client_id) // Client ID, Provided by wallet
			->setClientSecret($this->client_secret) // Client Secret, Provided by wallet
			->setTestMode($this->test_mode) // Test mode, default is false, can be skipped
			->setTransactionId($t_i) // Transaction id of authorization transaction
			->setAmount($amount); // The amount to capture in currency's smallest denomination (e.g amount in pounds x 100)

		$result = $transaction->send();

		if (!empty($transaction->getError()))
			return [
				'status' => 0,
				'data' => $transaction->getError()
			];
			
		return [
			'status' => 1,
			'data' => $result
		];
	}


	/**
	* Cancel a charge transaction
	*@param  $t_i // Transaction id of authorization transaction
	*@param $amount // The amount to capture in currency's smallest denomination (e.g amount in pounds x 100)
	**/
	public function setCancel(string $t_i, int $amount){

		$transaction = (new Cancel())
			->setClientId($this->client_id) // Client ID, Provided by wallet
			->setClientSecret($this->client_secret) // Client Secret, Provided by wallet
			->setTestMode($this->test_mode) // Test mode, default is false, can be skipped
			->setTransactionId($t_i) // Transaction id of authorization transaction
			->setAmount($amount)// The amount to capture in currency's smallest denomination (e.g amount in pounds x 100)
			->setSourceCode(''); // Source code, provided by wallet

		$result = $transaction->send();

		if (!empty($transaction->getError()))
			return [
				'status' => 0,
				'data' => $transaction->getError()
			];
			
		return [
			'status' => 1,
			'data' => $result
		];
	}



	/**
	* Is possible to get charge token at backend.
	* It may be required in custom integration, more details can be found here: https://developer.vivawallet.com/online-checkouts/native-checkout-v2/
	* @param $card // All the info of the card to make the charge
	* @param $url_redirect // Url to redirect when authentication session finished
	**/
	public function getChargeTokenAtBackend(array $card, string $url_redirect){

		$transaction = (new ChargeToken())
			->setClientId($this->client_id) // Client ID, Provided by wallet
			->setClientSecret($this->client_secret) // Client Secret, Provided by wallet
			->setTestMode($this->test_mode) // Test mode, default is false, can be skipped
			->setAmount($card['amount']) // The amount in currency's smallest denomination (e.g amount in pounds x 100)
			->setCvc($card['cvc']) // Card cvc code
			->setNumber($card['card_number']) // Card number
			->setHolderName($card['holder_name']) // Card holder name
			->setExpirationYear($card['expiration_year']) // Card expiration year
			->setExpirationMonth($card['expiration_month']) // Card expiration month
			->setSessionRedirectUrl($url_redirect); // Url to redirect when authentication session finished
		$result = $transaction->send();

		if (!empty($transaction->getError()))
			return [
				'status' => 0,
				'data' => $transaction->getError()
			];
			
		// Get charge token
		// $chargeToken = $result->chargeToken;
		// $redirectToACSForm = $result->redirectToACSForm;	
		return [
			'status' => 1,
			'data' => $result
		];

	}

	/**
	* Check for installments
	* Retrieve the maximum number of installments allowed on a card.
	*@param $card_number // Number of the credit card
	**/
	public function getInstalments(string $card_number){

		$transaction = (new Installments())
			->setClientId($this->client_id) // Client ID, Provided by wallet
			->setClientSecret($this->client_secret) // Client Secret, Provided by wallet
			->setTestMode($this->test_mode) // Test mode, default is false, can be skipped
			->setNumber($card_number); // Card number

		$result = $transaction->send();

		if (!empty($transaction->getError()))
			return [
				'status' => 0,
				'data' => $transaction->getError()
			];
			
		// Get number of installments
		// $installments = $result->maxInstallments;
		return [
			'status' => 1,
			'data' => $result
		];
	}

创建模板

templates/admin/payment/native.html

<pre>

  <script type="text/javascript" src="https://www.vivapayments.com/web/checkout/v2/js"></script>

    <form action="" method="POST" id="payment-form" class="container pt-4">
      <div class="form-row">
        <label>
          <span>Name</span>
          <input type="text" size="20" name="name" autocomplete="off" value="Pedro V" />
        </label>
      </div>
      <div class="form-row">
        <label>
          <span>Phone</span>
          <input type="text" size="20" name="phone" autocomplete="off" value="963963963" />
        </label>
      </div>
      <div class="form-row">
        <label>
          <span>Email</span>
          <input type="text" size="20" name="email" autocomplete="off" value="vgspedro@gmail.com" />
        </label>
        </div>  
        <div class="form-row">
          <label>
            <span>Cardholder Name</span>
            <input type="text" size="20" name="txtCardHolder" autocomplete="off" data-vp="cardholder" value="Pedro" />
            </label>
        </div>

        <div class="form-row">
            <label>
                <span>Card Number</span>
                <input type="text" size="20" name="txtCardNumber" autocomplete="off" data-vp="cardnumber" value="4111111111111111" />
            </label>
        </div>
        <div class="form-row">
            <label>
                <span>CVV</span>
                <input type="text" name="txtCVV" size="4" autocomplete="off" data-vp="cvv" value="111" />
            </label>
        </div>
        <div class="form-row">
          <label>
            <span>Expiration (MM/YYYY)</span>
          </label>
          <input type="text" size="2" name="txtMonth" autocomplete="off" data-vp="month" value="10" />
          <span> / </span>
          <input type="text" size="04" name="txtYear" autocomplete="off" data-vp="year" value="2024" />
        </div>
        <input name="token" type="hidden">
           <div class="form-row">
        <label title="Check your VivaWallet account to see the current status of the Payment"> Payment Actions
          <select name="action">
            <option value="charge">Charge Only</option>
            <option value="authorization">Authorized</option>
            <option value="charge_capture">Charge & Capture</option>
            <option value="charge_cancel">Charge & Cancel</option>
          </select>
        </label>
      </div>
        <button class="btn btn-success" type="button" id="submit">Submit Payment </button>
    </form>
    <hr>
	<h3>Options</h3>
	Charge Only = Create a transaction to be Captured<br>
	Authorized = Create a transaction and Captured the amount<br>
	Charge & Capture = Create a transaction then Capture the amount<br>
	Charge & Cancel = Create a transaction then Cancel the transaction<br>


    <div id="threed-pane" style="height: 450px;width:500px"></div>

    <script type="text/javascript">
      $(document).ready(function () {
        VivaPayments.cards.setup({
          baseURL: '{{ payment_url }}',
          authToken: '{{ viva_token }}',
          cardHolderAuthOptions: {
            cardHolderAuthPlaceholderId: 'threed-pane',
              cardHolderAuthInitiated: function () {
                $('#threed-pane').show();
              },
              cardHolderAuthFinished: function () {
                $('#threed-pane').hide();
              }
            },
            installmentsHandler: function (response) {
              if (!response.Error) {
                if (response.MaxInstallments == 0)
                  return;
                $('#drpInstallments').show();
                for (i = 1; i <= response.MaxInstallments; i++) {
                  $('#drpInstallments').append($("<option>").val(i).text(i));
                }
              }
              else {
                toastr['error'](response.Error);
              }
            }
          });
          $('#submit').on('click', function (evt) {
            evt.preventDefault();
            VivaPayments.cards.requestToken({
              amount: {{amount}}
            }).done(function (data) {

              $('[name=token]').val(data.chargeToken)

              $('.loader').removeClass('d-none');
                setTimeout(function(){
                $.ajax({  
                  url:'{{path("payment_submit")}}',
                  type: "POST",
                  data: $('#payment-form').serialize(),
                  cache: false,
                  success: function(data){  
                    $('.loader').addClass('d-none');
                    console.log(data)
                    if (data.status == 1){
                      toastr['success']('{%trans%}success{%endtrans%} - Transaction '+data.message.transactionId);
                    }
                    else if (data.status == 0){
                      toastr['info'](data.message);
                    }
                    else{
                      for(var i in data.data)
                        obj += data.data[i]+'<br>';
                      toastr['info'](obj);
                  }
                },
                error:function(data){
                  $('.loader').addClass('d-none');
                  toastr['error']('{%trans%}wifi_error{%endtrans%}');
                }
              })
            }, 500)
            console.log(data);
          })
          .fail(function(jqXHR, textStatus){
            console.log(jqXHR.Error)
            statusCodes(jqXHR.Error.ErrorCode, jqXHR.Error.ErrorText)
          })
        });
      });

    function statusCodes(code, error){
      code = Number(code)
      if(code >= 400 && code <= 499)
        toastr['info'](code+' '+error);
      else if (code >= 500 && code <= 599)
        toastr['error'](code+' '+error);
      else
      toastr['error']('Internet connection'); 
    }

  </script>

添加路由

config/routes.yaml

payment: path: /admin/payment controller: App\Controller\PaymentController::index

payment_submit: path: /admin/payment-submit controller: App\Controller\PaymentController::submit condition: 'request.isXmlHttpRequest()' methods: [POST]

预授权 https://developer.vivawallet.com/api-reference-guide/payment-api/#tag/Payments/paths/~1api~1orders/post

预授权布尔值 默认:false 如果为true,则执行预授权交易。这将在一段时间内将所选金额作为不可用(而不向客户收费)。

使用借记卡或信用卡进行的预授权交易将余额保留为不可用,直到商家清除交易或保留“掉落”。在借记卡的情况下,授权保留可能在任何交易日期后1-5天内从账户中“掉落”(从而使余额再次可用),具体取决于银行的策略;在信用卡的情况下,保留期可能长达30天,具体取决于发卡行。

先决条件

完成https://developer.vivawallet.com/online-checkouts/native-checkout-v2/中的先决条件步骤,并获取您的Client IDClient Secret。您需要设置一个支付源,将本地结账V2作为集成方法,并获取Source Code

获取卡片收费令牌

如在此处所述,在前端创建支付表单和Charge Tokenhttps://developer.vivawallet.com/online-checkouts/native-checkout-v2/ 您需要在前端拥有Access TokenBase URL,并且您可以通过以下方式获取它们

单元测试

通过./vendor/bin/phpunit tests运行测试。尽管库代码设计为与php 5.6兼容,但由于phpunit版本为9,测试需要至少php 7.3