Home  >  Article  >  Backend Development  >  Sample code sharing for Paypal to implement recurring deduction (subscription) function

Sample code sharing for Paypal to implement recurring deduction (subscription) function

黄舟
黄舟Original
2017-03-24 09:26:062986browse

This article mainly introduces the ideas and methods of Paypal to implement recurring deductions (subscriptions); it also summarizes how to use Paypal's payment interface , which has a good reference value. Let’s take a look with the editor below

Cause

The business needs to integrate Paypal to realize the recurring deduction function. However, Baidu and GOOGLE have gone around, except the official website In addition, I couldn't find any relevant development tutorials, so I had to look at Paypal. After two days, the integration was successful. Here is a summary of how to use Paypal's payment interface.

Paypal now has multiple sockets:

  • Express Checkout is implemented through Braintree (we will talk about Braintree later);

  • Create App through the interface of REST Api (now the mainstream interface);

  • The interface of NVP/SOAP API apps (old interface);

Braintree's interface

Braintree is a company acquired by Paypal. In addition to supporting Paypal payment, it also provides upgrade plans, credit cards, customer information, etc. A complete series of management, more convenient to use; Paypal's second set of REST interface actually integrates most of these functions, but Paypal's Dashboard cannot directly manage this information but Braintree can, so I actually prefer to use Braintree. The key is that the backend framework I use is Laravel, and its cashier solution supports Braintee by default, so this set of interfaces is my first choice. But when I implemented all its functions, I found a painful problem: Braintree is not supported in China. . . . . . Death. . .

REST API

This is a product that adapts to the development of the times. If you have used OAuth 2.0 and REST API before, you should not have any confusion when looking at these interfaces.

Old interface

Unless the REST API interface cannot be satisfied, such as policy restrictions, it is not recommended to use it. The whole world is migrating to the OAuth 2.0 authentication method and the API usage method of REST API, so why go against the trend? Therefore, when the REST API can solve the problem, I did not make an in-depth comparison of this set of interfaces.

Introduction to REST API

It is still very cumbersome to adjust these APIs directly. At the same time, we just want to complete the business requirements as soon as possible instead of getting into an in-depth understanding of the API.

So how to get started? It is recommended to directly install the officially provided PayPal-PHP-SDK and use its Wiki as a starting point.

Before completing the first example, please make sure you have a Sandbox account and configured it correctly:

  • Client ID

  • Client Secret

  • Webhook API (must start with https and be port 443, localDebuggingIt is recommended to use ngrok reverse proxy to generate the address)

  • Returnurl (note the same as above)

  • Payments This is a one-time payment interface and does not support recurring donations. The main payment content includes support for Paypal payment, credit card payment, support through saved credit card (requires the use of Vault interface, such an interface is mainly due to PCI requirements, does not allow general websites to collect sensitive credit card information), supports payment to the third Third party payee.

  • Payouts Not used, ignore;

  • Authorization and Capture supports logging in to your website directly through your Paypal account and obtains relevant information;

  • Sale Follow Relevant to the mall, not used, ignored;

  • Order Relevant to the mall, not used, ignored;

  • Billing Plan & Agreements Upgrade plan and contract, that is, the subscription function, you must use the function here to achieve recurring deductions, which is the focus of this article;

  • Vault Store credit card information

  • ##Payment Experience Not used, ignored;

  • Notifications Process the information of Webhook, Important, but not the focus of this article;

  • Invoice Invoice processing;

  • Identity Authentication processing, realizing OAuth 2.0 login, and obtaining the corresponding token to request other APIs. This Paypal-PHP-SDK has already been completed and will not be discussed in this article.

    How to implement recurring deductions

    Four steps:

    1. Create an upgrade plan and activate it;

    2. Create a subscription (create Agreement), and then jump to Paypal's website to wait for user consent;

    3. After the user agrees, execute the subscription

    4. Get the debit bill

    1. Create an upgrade plan

    The upgrade plan corresponds to the Plan class. There are several points to note in this step:

    • After the upgrade plan is created, it is in the CREATED state and must be changed to ACTIVE before it can be used normally.

    • Plan has two objects, PaymentDefinition and MerchantPreferences, both of which cannot be empty;

    • If you want to create a TRIAL type plan, The plan must also have a matching REGULAR payment definition, otherwise an error will be reported;

    • Look at the code and call a setSetupFee (very, very, very important) method, which sets the completion of the subscription The fee for the first debit after that, while the recurring deduction method of the Agreement object sets the fee at the beginning of the second time.

    Take creating a Standard plan as an example, its parameters are as follows:

    $param = [
     "name" => "standard_monthly",
     "display_name" => "Standard Plan",
     "desc" => "standard Plan for one month",
     "type" => "REGULAR",
     "frequency" => "MONTH",
     "frequency_interval" => 1,
     "cycles" => 0,
     "amount" => 20,
     "currency" => "USD"
     ];

    The code to create and activate the plan is as follows:

     //上面的$param例子是个数组,我的实际应用传入的实际是个对象,用户理解下就好。
     public function createPlan($param)
     {
     $apiContext = $this->getApiContext();
     $plan = new Plan();
     // # Basic Information
     // Fill up the basic information that is required for the plan
     $plan->setName($param->name)
     ->setDescription($param->desc)
     ->setType('INFINITE');//例子总是设置为无限循环
     // # Payment definitions for this billing plan.
     $paymentDefinition = new PaymentDefinition();
     // The possible values for such setters are mentioned in the setter method documentation.
     // Just open the class file. e.g. lib/PayPal/Api/PaymentDefinition.php and look for setFrequency method.
     // You should be able to see the acceptable values in the comments.
     $paymentDefinition->setName($param->name)
     ->setType($param->type)
     ->setFrequency($param->frequency)
     ->setFrequencyInterval((string)$param->frequency_interval)
     ->setCycles((string)$param->cycles)
     ->setAmount(new Currency(array('value' => $param->amount, 'currency' => $param->currency)));
     // Charge Models
     $chargeModel = new ChargeModel();
     $chargeModel->setType('TAX')
     ->setAmount(new Currency(array('value' => 0, 'currency' => $param->currency)));
     $returnUrl = config('payment.returnurl');
     $merchantPreferences = new MerchantPreferences();
     $merchantPreferences->setReturnUrl("$returnUrl?success=true")
     ->setCancelUrl("$returnUrl?success=false")
     ->setAutoBillAmount("yes")
     ->setInitialFailAmountAction("CONTINUE")
     ->setMaxFailAttempts("0")
     ->setSetupFee(new Currency(array('value' => $param->amount, 'currency' => 'USD')));
     $plan->setPaymentDefinitions(array($paymentDefinition));
     $plan->setMerchantPreferences($merchantPreferences);
     // For Sample Purposes Only.
     $request = clone $plan;
     // ### Create Plan
     try {
     $output = $plan->create($apiContext);
     } catch (Exception $ex) {
     return false;
     }
     $patch = new Patch();
     $value = new PayPalModel('{"state":"ACTIVE"}');
     $patch->setOp('replace')
     ->setPath('/')
     ->setValue($value);
     $patchRequest = new PatchRequest();
     $patchRequest->addPatch($patch);
     $output->update($patchRequest, $apiContext);
     return $output;
     }

    2. Create a subscription (create Agreement), and then jump to Paypal's website to wait for the user's consent

    After the Plan is created, how do you let users subscribe? In fact, it is to create an Agreement. Regarding Agreement, there are the following points to note:

    • As mentioned earlier, the setSetupFee method of the Plan object sets the fee for the first deduction after completing the subscription, while the cyclic deduction method of the Agreement object The fee is set at the beginning of the second time.

    • The setStartDate method sets the time for the second deduction, so if you cycle on a monthly basis, it should be the current time plus one month. At the same time, this method requires that the time format is ISO8601 format. , can be easily solved using the Carbon library;

    • When creating the Agreement, the unique ID has not been generated yet, so I encountered a small difficulty: that is when the user completes the subscription , how do I know which user this subscription belongs to? The token in the URL obtained through Agreement's getApprovalLink method is unique. I extract the token as a method of identification and replace it with the real ID after the user completes the subscription.

    The example parameters are as follows:

    $param = [
     'id' => 'P-26T36113JT475352643KGIHY',//上一步创建Plan时生成的ID
     'name' => 'Standard', 
     'desc' => 'Standard Plan for one month'
    ];

    The code is as follows:

     public function createPayment($param)
     {
     $apiContext = $this->getApiContext();
     $agreement = new Agreement();
     $agreement->setName($param['name'])
     ->setDescription($param['desc'])
     ->setStartDate(Carbon::now()->addMonths(1)->toIso8601String());
     // Add Plan ID
     // Please note that the plan Id should be only set in this case.
     $plan = new Plan();
     $plan->setId($param['id']);
     $agreement->setPlan($plan);
     // Add Payer
     $payer = new Payer();
     $payer->setPaymentMethod('paypal');
     $agreement->setPayer($payer);
     // For Sample Purposes Only.
     $request = clone $agreement;
     // ### Create Agreement
     try {
     // Please note that as the agreement has not yet activated, we wont be receiving the ID just yet.
     $agreement = $agreement->create($apiContext);
     // ### Get redirect url
     // The API response provides the url that you must redirect
     // the buyer to. Retrieve the url from the $agreement->getApprovalLink()
     // method
     $approvalUrl = $agreement->getApprovalLink();
     } catch (Exception $ex) {
     return "create payment failed, please retry or contact the merchant.";
     }
     return $approvalUrl;//跳转到$approvalUrl,等待用户同意
     }

    Function Returns $approvalUrl after execution, remember to pass redirect( $approvalUrl) jumps to Paypal's website to wait for user payment.

    After the user agrees, execute the subscription

    After the user agrees, the subscription has not been completed. The execute method of Agreement must be executed to complete the real process. subscription. The important thing to note in this step is that

    • After completing the subscription, it does not mean that the payment will be deducted, and it may be delayed for a few minutes;

    • If the first step If the setSetupFee fee is set to 0, the order will not be generated until the recurring deduction time is up;

    The code snippet is as follows:

     public function onPay($request)
     {
     $apiContext = $this->getApiContext();
     if ($request->has('success') && $request->success == 'true') {
     $token = $request->token;
     $agreement = new \PayPal\Api\Agreement();
     try {
     $agreement->execute($token, $apiContext);
     } catch(\Exception $e) {
     return ull;
     return $agreement;
     }
     return null;
     }

    Get transaction records

    After subscribing, the transaction record of transaction deduction may not be generated immediately. If it is empty, try again after a few minutes. Notes on this step:

    • start_date and end_date cannot be empty

    • In actual testing, the object returned by this function cannot always return empty JSON object, so if you need to output JSON, please manually extract the corresponding parameters according to the API description of AgreementTransactions.

     /** 获取交易记录
     * @param $id subscription payment_id
     * @warning 总是获取该subscription的所有记录
     */
     public function transactions($id)
     {
     $apiContext = $this->getApiContext();
     $params = ['start_date' => date('Y-m-d', strtotime('-15 years')), 'end_date' => date('Y-m-d', strtotime('+5 days'))];
     try {
     $result = Agreement::searchTransactions($id, $params, $apiContext);
     } catch(\Exception $e) {
     Log::error("get transactions failed" . $e->getMessage());
     return null;
     }
     return $result->getAgreementTransactionList() ;
     }

    Finally, of course Paypal also has a corresponding official tutorial, but it calls the native interface. The difference with the process above is that it only talks about the first three steps

    Issues that need to be considered

    The function has been implemented, but many attention points have also been found:

    • The connection is particularly slow when using Sandbox for testing in China. It often prompts a timeout or error, so special consideration needs to be given to the situation where the user closes the page mid-execution;

    • Be sure to implement webhook, otherwise when the user cancels the subscription in Paypal, your website will not be able to Notice;

    • Once a subscription (Agreement) is generated, it will remain in effect unless canceled actively. Therefore, if your website is designed with multiple upgrade plans (such as Basic, Standard, Advanced), when the user has subscribed to a plan and switches to an upgrade plan, the previous upgrade plan must be canceled during development;

    • The user agrees to subscribe - (cancel the old subscription - complete the signing of the new subscription - modify the user information to the new subscription), the whole process of brackets should be an atomic operation, and it is time-consuming and long. Therefore, it should be put into the queue until it succeeds and the experience will be better.

    The above is the detailed content of Sample code sharing for Paypal to implement recurring deduction (subscription) function. For more information, please follow other related articles on the PHP Chinese website!

    Statement:
    The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn