Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for Paypal express checkout and drupal (updated + smart buttons)
Nicholas Babu
Nicholas Babu

Posted on • Edited on • Originally published atflaircore.com

     

Paypal express checkout and drupal (updated + smart buttons)

A while ago, I wrotea blog demonstrating how one could integrate PayPal express checkout in Drupal, but...; in tech, things move(change) fast, mostly for better:
that approach had some limitations like; handlingcredit cards, the user checkout experience was not as 'great', the PayPal SDK library we were extending has beenabandoned (deprecated) fora better one (which was recently deprecatedbut no other alternative, so here we're ).

In this blog we will attempt to achieve the same and make things better.
Code for thisblog is available on @github.
And a live example available@https://flaircore.com, you're welcome(invited) to test with real money, which will be spent on your behalf.

In an age of smart phones, fridges, rings, contracts..., we will implementsmart payment buttons, not because smart is trendy, but because of the user experience and the added security around it.
Smart buttons can be used to initiate, authorize and capture an order, but in this blog, we will only initiate and authorize, and add the necessary logic in Drupal to process (capture) the authorized order (payment) details in therequest object.

All this will be done without leaving the page the button is on.

Prerequisites:

  • Working instance of Drupal where dependencies are managed via composer.
  • Basic understanding ofDrupal module structure.

A look into the module structure (paypal_example):

paypal_example_module_file_structurepaypal_example_module_file_structure

Below, we will go through the contents of each file and it's purpose in this project (it's a prerequisite to at least be familiar with Drupal module structure/development), starting with the base files and later with the sub folders.

paypal_example.info.yml

This files defines the module and makes it discoverable by Drupal to install, it also contains the supported core versions and a 'configure' key with a value of 'paypal_example.paypal_settings_form' which is a route id defined in the routing.yml (more to that later). This renders a configure link below this module description on the modules' listing page.

configure module link

paypal_example.libraries.yml

Defines the javascipt file(s) and their dependencies that will enable us to implement the smart buttons on the client side.

paypal_example.module

Contains a hook_theme() implementation, registering thetemplates\paypal-example.html.twig file for use by
any part of Drupal that wants to render that template.

paypal_example.routing.yml

Defines two routes, one to render the config form (src\Form\PayPalPaymentsConfigForm.php) and the other to render the page we will be testing the paypal functionality as defined in thesrc\Controller\PayPalController.php file, and finally

paypal_example.services.yml

Registers thesrc\PayPalClient.php class definition as a Drupal service for use by any Drupal module that wants to.

We will now go through the contents of each file below before moving on with how the each relate with the subfolders and their files.

# paypal_example.info.ymlname:'Paypalexample'type:moduledescription:'PaypalpaymentexampleforDrupal'package:'Payment'core_version_requirement:^8.8.0 || ^9 || ^10configure:paypal_example.paypal_settings_form# End of file.
Enter fullscreen modeExit fullscreen mode
#paypal_example.libraries.yml# https://github.com/paypal/paypal-js#legacy-browser-supportpaypal_cdn:version:VERSIONheader:truejs://unpkg.com/@paypal/paypal-js@5.1.1/dist/iife/paypal-js.min.js:{type:external,minified:true,attributes:{}#attributes: { defer: true, async: true}# window.paypalLoadScript is not a function error, see# https://github.com/paypal/paypal-js; should be managed# via npm as setting attributes to defer and async# causes the error above sometimes.}paypal:js:js/paypal_example.js:{attributes:{defer:true,async:true}}dependencies:-core/jquery-core/drupalSettings-paypal_example/paypal_cdn# End of file.
Enter fullscreen modeExit fullscreen mode
<?php/** * @file * Contains paypal_example.module. */useDrupal\Core\Routing\RouteMatchInterface;/** * Implements hook_help(). */functionpaypal_example_help($route_name,RouteMatchInterface$route_match){switch($route_name){// Main module help for the paypal_example module.case'help.page.paypal_example':$output='';$output.='<h3>'.t('About').'</h3>';$output.='<p>'.t('Paypal example checkout').'</p>';return$output;default:}}/** * Implements hook_theme(). */functionpaypal_example_theme(){return['paypal_example'=>['render element'=>'children','template'=>'paypal-example','variables'=>['data'=>NULL,]]];}
Enter fullscreen modeExit fullscreen mode
# paypal_example.routing.ymlpaypal_example.paypal_settings_form:path:'/admin/config/paypal_example/settings'defaults:_form:'\Drupal\paypal_example\Form\PayPalPaymentsConfigForm'_title:'YourPayPalpaymentssettings'requirements:_permission:'administerpaypal_payments'paypal_example.payment_example:path:'/flair-core/paypal_payment'defaults:_controller:'\Drupal\paypal_example\Controller\PayPalController::pay'_title:'paywithPaypal'requirements:_permission:'accesscontent'# End of file.
Enter fullscreen modeExit fullscreen mode
# paypal_example.services.ymlservices:paypal_example.paypal_client:class:Drupal\paypal_example\PayPalClientarguments:[]calls:-[setConfig,['@config.factory']]-[setEntity,['@entity_type.manager']]# End of file.
Enter fullscreen modeExit fullscreen mode

Recap so far.

  • In the services.yml file, the service idpaypal_example.paypal_client is well isolated and re-usable, it relies on theV2 php SDK we are extending, which will also make it easier to swap between newer sdks in the future if need be.
  • In the libraries.yml, the library idpaypal_cdn which is required by the library idpaypal below it as a dependency, definespaypal.js with legacy browser support to make sure the buttons' functionality is consistent across browsers. Even though the recommended way is to usea package manager with paypal.js, I made that compromise to keep the scope of this blog relevant to the topic, omitting some important attributes (paypal_cdn lib definitions) to make sure that library is loaded before the library that depends on it is.

Subfolder section:

For the sub folders and 'files', we will start with those that interact with Drupal 'internal' apis and finish up the once that interact with the user facing apis (Render/Controller).
Before proceeding to implement thepaypal_example.paypal_client service definitions (services.yml file) let's install the paypal sdk it relies on by runningcomposer require paypal/paypal-checkout-sdk inside the Drupal root directory.

src\PayPalClient.php

This file relies on 3 main variants;

  • paypal_example.settings configuration entity id, updatable via thePayPalPaymentsConfigForm definitions.
  • PayPalCheckoutSdk\Core\PayPalHttpClient which is part of the php package we installed earlier and can be independently swappable as you wish.
  • paypal_payment_example entity id as defined in thesrc\Entity\PayPalPaymentExample.php file

This class contains two public methods we can use with our class instance anywhere, these are;

  • getConfigs(), which returns values ofpaypal_example.settings configuration entity as an array.
  • captureOrder(), takes two arguments; $order_id and $sku, the order_id is the token PayPay gives an authorized order, once a client has gone through their api authentification, and is passed to the request, while sku is any unique string to identify the product item associated with this order. This method also creates a new entry ofpaypal_payment_example entity content with the values from the captured order.

templates\paypal-example.html.twig

Defines the content markup (see hook_theme() in .module file) and contains the amount input field and an empty html dom to anchor the PayPal smart buttons defined in the filejs\paypal_example.js

js\paypal_example.js

This file depends on<module-id>/paypal_cdn defined in the .libraries.yml file, and defines a way to create an order with the data passed from the Controller asdrupalSettings.paypal_payment_data and the amount input field value, it also redirects an approved order to the current page, with the relevant url params as defined in the onAprrove call back in thepaypal.Buttons({}) object.

Below is the contents of each of these three files in details.

<?php# PayPalClient.phpnamespaceDrupal\paypal_example;useDrupal\Core\Config\ConfigFactoryInterface;useDrupal\Core\Entity\EntityTypeManagerInterface;usePayPalCheckoutSdk\Core\PayPalHttpClient;usePayPalCheckoutSdk\Core\SandboxEnvironment;usePayPalCheckoutSdk\Core\ProductionEnvironment;usePayPalCheckoutSdk\Orders\OrdersCaptureRequest;usePayPalHttp\HttpException;classPayPalClient{/**   * @var \Drupal\Core\Config\ConfigFactoryInterface   */protected$configFactory;/** @var  \Drupal\Core\Entity\EntityTypeManagerInterface */protected$entityTypeManager;publicfunctionsetConfig(ConfigFactoryInterface$configFactory){$this->configFactory=$configFactory;}publicfunctionsetEntity(EntityTypeManagerInterface$entityTypeManager){$this->entityTypeManager=$entityTypeManager;}publicfunctiongetConfigs(){$config=$this->configFactory->getEditable('paypal_example.settings');$client_id=$config->get('client_id');$client_secret=$config->get('client_secret');$environment=$config->get('environment');$store_currency=$config->get('currency');$payment_title=$config->get('payment_title');return['client_id'=>$client_id,'client_secret'=>$client_secret,'environment'=>$environment,'currency'=>$store_currency,'payment_title'=>$payment_title,];}/**   * Returns PayPal HTTP client instance with environment that has access   * credentials context. Use this instance to invoke PayPal APIs, provided the   * credentials have access.   */publicfunctionclient(){returnnewPayPalHttpClient($this->environment());}/**   * Set up and return PayPal PHP SDK environment with PayPal access credentials.   * This sample uses SandboxEnvironment. In production, use LiveEnvironment.   */protectedfunctionenvironment(){//$config=$this->getConfigs();$clientId=getenv("PP_CLIENT_ID")?:$config['client_id'];$clientSecret=getenv("PP_CLIENT_SECRET")?:$config['client_secret'];if($config['environment']==='sandbox'){returnnewSandboxEnvironment($clientId,$clientSecret);}elsereturnnewProductionEnvironment($clientId,$clientSecret);}/**   * @param $order_id   *   *//**   * @param $order_id   * the APPROVED-ORDER-ID   * @param $sku   *  The product sku   *   * @return array   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException   * @throws \Drupal\Core\Entity\EntityStorageException   * @throws \PayPalHttp\IOException   */publicfunctioncaptureOrder($order_id,$sku){$request=newOrdersCaptureRequest($order_id);$request->prefer('return=representation');try{// Call API with your client and get a response for your call$response=$this->client()->execute($request);//$status_code = $response->statusCode;$status=$response->result->status;$id=$response->result->id;$email_address=$response->result->payer->email_address;//$intent = $response->result->intent;$currency_code=$response->result->purchase_units[0]->amount->currency_code;$payments_id=$response->result->purchase_units[0]->payments->captures[0]->id;$amount=$response->result->purchase_units[0]->amount->value;$values=['payer_email'=>$email_address,'amount'=>$amount,'transaction_id'=>$payments_id,'sale_id'=>$id,'payment_status'=>$status,'invoice_id'=>$id,'sku'=>$sku,];$entity=$this->entityTypeManager->getStorage('paypal_payment_example');$entity->create($values)->save();# TODO:: add event emitter above\Drupal::messenger()->addMessage(t("Transaction completed for$amount$currency_code ."));return$values;}catch(HttpException$ex){\Drupal::messenger()->addError('Issue completing the transaction : '.$ex->getMessage());return['error'=>$ex->getMessage()];}}}# End of file
Enter fullscreen modeExit fullscreen mode
{# paypal-example.html.twig #}{%ifdata.client_id%}<divclass="content"><divstyle="padding-bottom: 1em; font-weight: 600">{{data.title}}</div><divid="paypal-example"class="paypal-example"style="padding-bottom: 1em;"><labelfor="amount">Amount in{{data.currency}} :</label><inputtype="number"id="amount"name="amount"min="5"value="{{data.amount}}"></div><divid="paypal-button-container"></div></div>{%else%}<div><h2> Please configure your app with the relevant info provided by paypal.</h2><span> Message :{{data.info}} .</span></div>{%endif%}{# End of file #}
Enter fullscreen modeExit fullscreen mode
// paypal_example.js(function($,Drupal,drupalSettings){constdata=drupalSettings.paypal_payment_dataif(!data.client_id){console.warn("Some information is missing!!")return}$(document).ready(function(){console.log(data)console.log(data)console.log(data)window.paypalLoadScript({"client-id":data.client_id}).then((paypal)=>{constamountInput=document.querySelector('#paypal-example input')constuniqueId=data.title+((Math.random()*Math.pow(36,6))|0).toString(36)paypal.Buttons({// Set up the transactioncreateOrder:function(dt,actions){// This function sets up the details of the transaction, including the amount and line item details.data.amount=amountInput.valueif(!data.amount){console.error('Please enter an amount')return}returnactions.order.create({intent:'CAPTURE',purchase_units:[{reference_id:uniqueId,custom_id:uniqueId,amount:{value:data.amount,currency_code:data.currency,breakdown:{item_total:{currency_code:data.currency,value:data.amount}}},items:[{name:data.title,description:data.title,sku:uniqueId,unit_amount:{currency_code:data.currency,value:data.amount},quantity:1},]}]});},onInit:function(dt,actions){// Btns initialized},onApprove:function(dt,actions){amountInput.value=data.amountwindow.location=`?&order_sku=${uniqueId}&order_id=${dt.orderID}`}}).render('#paypal-button-container');});})})(jQuery,Drupal,drupalSettings)// End of file
Enter fullscreen modeExit fullscreen mode

// End of file

Finally the last three files to complete this module.

src/Entity/PayPalPaymentExample.php

This file contains thepaypal_payment_example database table definitions, as well as getters and setters to use with any instance of this Class (Object).

src/Form/PayPalPaymentsConfigForm.php

Contains the markup (Form field definitions) and renders on the route idpaypal_example.paypal_settings_form that's defined in the .routing.yml file. The form writes topaypal_example.settings config entity with the new values everytime it's saved.

src/Controller/PayPalController.php

It's in this file that, almost everything we've worked on, so far, is wired to work with each other, and complete this payment's feature implementation. This file defines the content to render in thepaypal_example.payment_example route id, defined in the .routing.yml file. This include libraries too (js/css). Thepay method serves this route and supplies the $data array values to our template file and js file via the drupalSettings. Also the onApprove method, in the smart button implementation (js\paypal_example.js) redirects to this route, supplying some url params accordingly. This also handles the payment capture(charge) and save to database, once the order is authorized and the right values are available in the url params.

Below is the code that goes into each of these three files.

<?php# start of PayPalPaymentExample.phpnamespaceDrupal\paypal_example\Entity;useDrupal\Core\Entity\ContentEntityBase;useDrupal\Core\Entity\ContentEntityInterface;useDrupal\Core\Entity\EntityTypeInterface;useDrupal\Core\Field\BaseFieldDefinition;/** * Defines PayPalPaymentExample entity. * * @ingroup paypal_payment_example * * @ContentEntityType( *   id = "paypal_payment_example", *   label = @Translation("PayPalPaymentExample"), *   base_table = "paypal_payment_example", *   entity_keys = { *     "id" = "id", *     "uuid" = "uuid", *   }, * ) */classPayPalPaymentExampleextendsContentEntityBaseimplementsContentEntityInterface{/**   * {@inheritdoc}   */publicfunctiongetCreatedTime(){return$this->get('created')->value;}publicfunctiongetPayerEmail(){return$this->get('payer_email')->value;}publicfunctionsetPayerEmail($payer_email){$this->set('payer_email',$payer_email);return$this;}publicfunctiongetAmount(){return$this->get('amount')->value;}publicfunctionsetAmount($amount){$this->set('amount',$amount);return$this;}publicfunctiongetTransactionId(){return$this->get('transaction_id')->value;}publicfunctionsetTransactionId($transaction_id){$this->set('transaction_id',$transaction_id);return$this;}publicfunctiongetSaleId(){return$this->get('sale_id')->value;}publicfunctionsetSaleId($sale_id){$this->set('sale_id',$sale_id);return$this;}publicfunctiongetInvoiceId(){return$this->get('invoice_id')->value;}publicfunctionsetInvoiceId($invoice_id){$this->set('invoice_id',$invoice_id);return$this;}publicfunctiongetPaymentStatus(){return$this->get('payment_status')->value;}publicfunctionsetPaymentStatus($payment_status){$this->set('payment_status',$payment_status);return$this;}publicfunctiongetSku(){return$this->get('sku')->value;}publicfunctionsetSku($sku){$this->set('sku',$sku);return$this;}/**   * Determines the schema for slack_settings entity table   */publicstaticfunctionbaseFieldDefinitions(EntityTypeInterface$entity_type){// Standard field, used as unique if primary index.$fields['id']=BaseFieldDefinition::create('integer')->setLabel(t('ID'))->setDescription(t('The ID of the content entity.'))->setReadOnly(TRUE);// Standard field, unique outside of the scope of the current project.$fields['uuid']=BaseFieldDefinition::create('uuid')->setLabel(t('UUID'))->setDescription(t('The UUID of the content entity.'))->setReadOnly(TRUE);$fields['payer_email']=BaseFieldDefinition::create('string')->setLabel(t('Paypal email'))->setDescription(t('The Paypal email address'));$fields['amount']=BaseFieldDefinition::create('float')->setLabel(t('Amount'))->setDescription(t('The Amount in Store currency'));$fields['transaction_id']=BaseFieldDefinition::create('string')->setLabel(t('Transaction id'))->setSettings(array('max_length'=>60,))->setDescription(t('The unique transaction id generated by paypal'));$fields['sale_id']=BaseFieldDefinition::create('string')->setLabel(t('Sale ID'))->setSettings(array('max_length'=>60,))->setDescription(t('Stores the sale id in-case of a refund'));$fields['invoice_id']=BaseFieldDefinition::create('string')->setLabel(t('Invoice ID'))->setSettings(array('max_length'=>60,))->setDescription(t('The invoice number of the payment'));$fields['payment_status']=BaseFieldDefinition::create('string')->setLabel(t('Payment Status'))->setSettings(array('default_value'=>'','max_length'=>15,))->setDescription(t('Status either a Success or a Refund'));$fields['sku']=BaseFieldDefinition::create('string')->setLabel(t('SKU'))->setSettings(array('max_length'=>60,))->setDescription(t('The product SKU'));$fields['created']=BaseFieldDefinition::create('created')->setLabel(t('Created'))->setDescription(t('The time that the entity was created.'));return$fields;}}# End of file
Enter fullscreen modeExit fullscreen mode
<?php# start of PayPalPaymentsConfigForm.phpnamespaceDrupal\paypal_example\Form;useDrupal\Core\Form\ConfigFormBase;useDrupal\Core\Form\FormStateInterface;/** * Class PayPalPaymentsSettingsForm. * * Store the paypal credentials required to make the api calls */classPayPalPaymentsConfigFormextendsConfigFormBase{/**   * Gets the configuration names that will be editable.   *   * @return array   *   An array of configuration object names that are editable if called in   *   conjunction with the trait's config() method.   */protectedfunctiongetEditableConfigNames(){return['paypal_example.settings'];}/**   * {@inheritdoc}   */publicfunctiongetFormId(){return'paypal_example_settings_form';}/**   * {@inheritdoc}   */publicfunctionbuildForm(array$form,FormStateInterface$form_state){$config=$this->config('paypal_example.settings');$environmentTypes=['live'=>'Live','sandbox'=>'Sandbox',];$currency=['USD'=>'USD','GBP'=>'GBP','AUD'=>'AUD','CAD'=>'CAD','EUR'=>'EUR','JPY'=>'JPY'];$form['client_id']=['#type'=>'textfield','#title'=>$this->t('Client ID'),'#description'=>$this->t('The Client ID from PayPal, you can put any value here if you have set PP_CLIENT_ID in your environment variables'),'#default_value'=>$config->get('client_id'),'#maxlength'=>128,'#size'=>64,'#required'=>TRUE,];$form['client_secret']=['#type'=>'textfield','#title'=>$this->t('Client Secret'),'#description'=>$this->t('The Client Secret Key From PayPal, (You can put any value here, if you have set PP_CLIENT_ID in your env variables.)'),'#default_value'=>$config->get('client_secret'),'#maxlength'=>128,'#size'=>64,'#required'=>TRUE,];$form['environment']=['#type'=>'select','#title'=>$this->t('Environment'),'#options'=>$environmentTypes,'#description'=>$this->t('Select either; live or sandbox(for development)'),'#default_value'=>$config->get('environment'),'#required'=>TRUE,'#multiple'=>FALSE,];$form['currency']=['#type'=>'select','#title'=>$this->t('Store Currency'),'#options'=>$currency,'#description'=>$this->t('Select the currency to use with your store'),'#default_value'=>$config->get('currency'),'#required'=>TRUE,];$form['payment_title']=['#type'=>'textfield','#title'=>$this->t('Payment Title'),'#description'=>$this->t('The title to associate with this payment'),'#placeholder'=>'Example Payment','#default_value'=>$config->get('payment_title'),'#maxlength'=>180,'#size'=>120,'#required'=>TRUE,];$form['paypal_instructions']=['#type'=>'markup','#markup'=>$this->paypalDocumentation(),];$form['submit']=['#type'=>'submit','#value'=>$this->t('Save'),];return$form;}/**   * {@inheritdoc}   */publicfunctionvalidateForm(array&$form,FormStateInterface$form_state){parent::validateForm($form,$form_state);}/**   * {@inheritdoc}   */publicfunctionsubmitForm(array&$form,FormStateInterface$form_state){$env=$form_state->getValue('environment');$config=$this->config('paypal_example.settings');$config->set('currency',$form_state->getValue('currency'))->set('environment',$env)->set('client_secret',$form_state->getValue('client_secret'))->set('client_id',$form_state->getValue('client_id'))->set('payment_title',$form_state->getValue('payment_title'))->save();drupal_flush_all_caches();parent::submitForm($form,$form_state);}privatefunctionpaypalDocumentation(){return'    <div>    <p> <strong>Getting started. </strong></p>    <p>        * After logging in at: <a href="https://www.paypal.com/">Paypal.com</a> , got to        <a href="https://developer.paypal.com/developer/accountStatus/">Developer.paypal.com </a>    </p>    <p>        * Under <strong>Dashboard > My Apps & Credentials </strong>, click on Create App button, give it a        name, select a sandbox Business Account to use while testing, and confirm create.    </p>    <p>        * Note the <strong>Client ID</strong>,  and the <strong>Secret</strong> as those are required        in the above inputs.    </p>    </div>      <br>    ';}}# End of file
Enter fullscreen modeExit fullscreen mode
<?php# start of PayPalController.phpnamespaceDrupal\paypal_example\Controller;useDrupal\Core\Controller\ControllerBase;useDrupal\Core\Config\ConfigFactoryInterface;useDrupal\paypal_example\PayPalClient;useSymfony\Component\HttpFoundation\RedirectResponse;useSymfony\Component\HttpFoundation\Request;useSymfony\Component\DependencyInjection\ContainerInterface;classPayPalControllerextendsControllerBase{/**   * @var \Drupal\Core\Config\ConfigFactoryInterface   */protected$configFactory;/** @var \Drupal\paypal_example\PayPalClient */protected$paypalClient;publicfunction__construct(ConfigFactoryInterface$configFactory,PayPalClient$paypalClient){$this->configFactory=$configFactory;$this->paypalClient=$paypalClient;}publicstaticfunctioncreate(ContainerInterface$container){returnnewstatic($container->get('config.factory'),$container->get('paypal_example.paypal_client'),);}publicfunctionpay(Request$request){$config=$this->paypalClient->getConfigs();$currency=$config['currency'];$client_id=$config['client_id'];$payment_title=$config['payment_title'];$order_id=$request->get('order_id');$order_sku=$request->get('order_sku');if($order_sku&&$order_id){$capture_order=$this->paypalClient->captureOrder($order_id,$order_sku);if(isset($capture_order['payment_status'])){// Do something}(newRedirectResponse('/flair-core/paypal_payment'))->send();}$info=!$client_id?'Missing paypal app details.':null;$data=['title'=>$payment_title,'info'=>$info,'client_id'=>$client_id,'currency'=>$currency,'amount'=>10];return['#theme'=>'paypal_example','#cache'=>['max-age'=>0],'#attached'=>['library'=>'paypal_example/paypal','drupalSettings'=>['paypal_payment_data'=>$data]],// The items twig variable as an array as defined paypal_example_theme().'#data'=>$data,];}}# End of file.
Enter fullscreen modeExit fullscreen mode

The 'Test Drive!'

We have put some time and effort into this, so it's time to give it a test (be an end user).

  • Lets enable the module by navigating to the module listing page, checking on the Paypal example module from the modules list, and clicking install.
  • After the module is installed, a Configure link is available below it's description (still on the module listing page), click on that to go the paypal configuration form we created, another way to access the form would be going to /admin/config/paypal_example/settings url.

Paypal example configuration form

  • On a new tab, login tohttps://www.paypal.com/ and then got tohttps://developer.paypal.com/developer/accountStatus/ and under Dashboard > My Apps & Credentials, click on Create App,note the Client ID and Secret, as these are required in the configuration form. A sandbox user account to use with this app will be auto created for use (where test funds will be sent to/business owner), you will also be required to create a sandbox account(s) to test with as the buyer. A sandbox account lets you create a fake user (email, password and some bucks in that account) to test payments (only in sandbox environment).
  • Input the configuration form with the right info(details) and click save.
  • Head to /flair-core/paypal_payment url. (remember the route idpaypal_example.payment_example defined in the .routing.yml)

Payment btnPayment btn
From here, you can use one of the sandbox accounts you created earlier, as a buyer to test, and later upgrade to a live (real business account) account to collect funds as it fits your business/shop.

CONCLUSION:

From here, some improvements would include;

  • Managing our javascript dependencies via a package manager, to achieve the performance associated with that, and avoid running into issues, whenpaypal_example/paypal is loaded before thepaypal_example/paypal_cdn is.
  • We have payment entries/records in the database, a way to display that should be among the next things towards making this example module complete.
  • The php library we updated to, is also recently abandoned, but no other alternative is provided byPayPal, for this, all the logic that interacts with their api has been separated into it's own class (src\PayPalClient.php) so it's easier to change to any otherwrapper/SDK in the future.

RESOURCES:

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Full stack developer
  • Location
    Matrix
  • Work
    Full stack developer
  • Joined

More fromNicholas Babu

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp