+1 Daumen
7k Aufrufe

I stumbled over many questions regarding the implementation of the new Paypal Subscription API.

It is difficult because there is no PHP SDK for subscriptions and the documentation is hard to read.

Plus, you find many old tutorials, refering back to the old APIs.

So I decided to create a complete tutorial of how to integrate the new Paypal subscriptions with PHP and Javascript.

Prerequisites: You must have a standard paypal account (should be "business") and a developer account.

Okay, let's start:


1. Create Subscription Button in Paypal

In SANDBOX mode:
- Go to "Billing plans" https://sandbox.paypal.com/billing/plans (Tab Subscription Plans)
- Hit the button "Create Plan": https://sandbox.paypal.com/billing/plans/plan/create/choose-product?from=plans
- add your product ... choose "fix price", submit, on next page enter the price
- Product and Plan are the same for me (name of my digital good)
- click "activate plan"
- new page opens showing the button design: click button "Copy code" (bottom right)
- save the code in your editor

Do the same procedure for LIVE buttons:
https://www.paypal.com/billing/plans
https://www.paypal.com/billing/plans/plan/create/choose-product?from=plans


RESULT:

<div id="paypal-button-container-P-82B73303CA584772TMBOW7LA"></div>
<script src="https://www.paypal.com/sdk/js?client-id=AWJSVt2lvrjDK3eyMiicht1nOCRcFEHOhxBJS9enLCw6ZvQWB7zVfne1wzCBPcdJzaiagde5YIa2ZFbN&vault=true&intent=subscription" data-sdk-integration-source="button-factory"></script>
<script>
paypal.Buttons({
style: {
shape: 'rect',
color: 'gold',
layout: 'vertical',
label: 'subscribe'
},
createSubscription: function(data, actions) {
return actions.subscription.create({
/* Creates the subscription */
plan_id: 'P-82B73303CA584772TMBOW7LA'
});
},
onApprove: function(data, actions) {
alert(data.subscriptionID); // You can add optional success message for the subscriber here
}
}).render('#paypal-button-container-P-82B73303CA584772TMBOW7LA'); // Renders the PayPal button
</script>

Congratulations, with this code a user can create a subscription with you!


2. Set up your APP (site specific)

- login at https://developer.paypal.com/home
- go to dashboard https://developer.paypal.com/developer/applications/
- click "Create App"
- name the app and also add sandbox account (merchant)
- note: later on you need a second sandbox account to simulate the buyer. Create it.
- copy "SANDBOX API CREDENTIALS" from the page

Example:

Sandbox account
service-buyer@yoursite.com

Client ID
AYkqPe4ShL6LVIOBssEe834sdfsdfsfsfsdfklsjfhudfusfusdfzzjsdkfskdf12345

Secret
EJr5Y-MoCb97RRerd0Z5xfjsfsfdkfsdklfksjkfsdjksdflsdf7Q4MJpsQqy_7OhWxMWgGzxGxPgb1jh12345

- next scroll down and click "Add Webhook"
- Enter the URL you want to be informed about paypal events (your server URL, where the PHP script will be)
- e.g. URL https://www.yoursite.com/provider/paypal-webhook
- click on "all events"
- then "Save" button


3. PHP script (on your Server) to catch the Webhook data

- Create a PHP script at the location you specified (URL above) with the following code:

// WEBHOOK simulator: https://developer.paypal.com/developer/webhooksSimulator/
// WEBHOOK events: https://developer.paypal.com/docs/api-basics/notifications/webhooks/event-names/#subscriptions

// get data from Paypal Webhook POST
$data = json_decode(file_get_contents("php://input"));

error_log('PAYPAL EVENT: '.$data->event_type);

// write all data into server log - helpful for debugging and identifying available data
error_log(print_r($data, true));

// Next an example of how you can process and interact with the webhook data on your server:
if($data->event_type == 'BILLING.SUBSCRIPTION.ACTIVATED')
{
// paypal subscription id, e.g. "I-75NW8GLJYFDX"
$subscription_id = $data->resource->id;
$payeremail = $data->resource->subscriber->email_address;
$plan_id = $data->resource->plan_id;
$payerid = $data->resource->subscriber->payer_id;

// USERID
$userid = $data->resource->custom_id;

error_log($subscription_id); // e.g. "I-F5KXHVG4X0YX"
error_log('userid: '.$userid); // our own userid (defined in button javascript as "custom_id")

// handle the subscription
// Example: save subscription_id into our database
if(!empty($userid))
{
// very important, here we store the payerid (subscriptionid in our database)
$paydata = array(
'userid' => $userid,
'payprovider' => 'paypal',
'payerid' => $subscription_id,
'status' => 'active'
);
// your custom function
payprovider_create_entry($paydata);
}
else
{
error_log('# PAYPAL PROBLEM: userid not found: '.$payeremail);
}
}

else if($data->event_type == 'PAYMENT.SALE.COMPLETED')
{
// paypal subscription id, e.g. "I-75NW8GLJYFDX"
$subscription_id = $data->resource->billing_agreement_id;

// e.g. "29.95"
$payamount = $data->resource->amount->total;

// e.g. "completed"
$status = $data->resource->state;

// USERID (not the missing "_id")
$userid = $data->resource->custom;

error_log($subscription_id); // e.g. "I-F5KXHVG4X0YS"
error_log($payamount); // e.g. "29.95"
error_log($status); // e.g. "completed"
error_log('userid: '.$userid); // our own userid

// unlock user account as premium
if($status=='completed')
{
if(empty($userid))
{
  // user not found ...
}
else
{
// custom function you define yourself!
$userdata = get_customerdata($userid);

if(!empty($userdata))
{
// register payment and send email to customer
    // custom function you define yourself!
register_payment($userid, $payamount, 'paypal');
}
}
}
}

else if($data->event_type == 'PAYMENT.SALE.REFUNDED')
{
error_log('REFUND WEBHOOK DATA:');
error_log(print_r($data, true));

// paypal subscription id, e.g. "I-75NW8GLJYFDX"
$subscription_id = $data->resource->id;

// USERID
$userid = $data->resource->custom_id;
// *** $userid = $data->resource->custom; // not tested yet!

$payeremail = $data->resource->subscriber->email_address;

// e.g. "-29.95"
$payamount = $data->resource->amount->total;

// e.g. "completed"
$status = $data->resource->state;

error_log($subscription_id); // e.g. "I-F5KXHVG4X0YS"
error_log('userid: '.$userid); // our own userid

if(!empty($userid))
{
$userdata = get_usercustomerdata($userid);

// does user exist in DB
if(!empty($userdata))
{
// if money is lost, then rollback last added month from endtime
// your custom code
}
else
{
error_log('# PAYPAL PROBLEM: userid not found: '.$payeremail);
}
}

return;

Webhook done!

You can trigger some events with the webhook simulator and check your error logs, if the incoming data has arrived properly.

It should show (on Paypal) after the test is sent:

Your event has been successfully queued at {date}

Note that I use a $userid in the example code above that comes from custom_id. This is still an empty value (not set).

We have to add the custom_id when we use the paypal payment buttons on the sales page, with Javascript.


4. Implement paypal payment button on sales page

Note: Sandbox URL and live URL are the same: https://www.sandbox.paypal.com/sdk/js

$planid = 'P-9U5126551T923450KMBNASXX';
// if you have several plans, you want to decide here about other plans

// paypal credentials (sandbox for now)
$clientid = "AU_d-ShIOx.......";
$secret = "EObgYm5a.......";

// change paypal URL params for customization, see https://developer.paypal.com/docs/checkout/reference/customize-sdk/
// "locale=de_DE" switches to German
// "disable-funding=credit,card" hides the creditcard button

$output .= '
<div class="paypal-wrap">
<div id="paypal-button-container-'.$planid.'" class="paypal-button-wrap"></div>
</div>

<script src="https://www.sandbox.paypal.com/sdk/js?client-id='.$clientid.'&vault=true&intent=subscription&locale=de_DE&disable-funding=credit,card" data-sdk-integration-source="button-factory"></script>

<script>

paypal.Buttons({
style: {
shape: "rect",
color: "gold",
layout: "vertical",
label: "subscribe"
},
createSubscription: function(data, actions) {
return actions.subscription.create({
plan_id: "'.$planid.'",
custom_id: "'.$userid.'",
application_context: {
shipping_preference: "NO_SHIPPING" // no shipping since digital good
},
});
},
onApprove: function(data, actions)
{
// console.log(data.subscriptionID); // further process if needed
$(".paypal-wrap").hide();
// show your success confirmation
},
onCancel: function (data) {
// show cancel page, or return to cart
},
onError: function (err) {
// window.location.href = "/error-page";
alert("Payment Error: "+err);
}
}).render("#paypal-button-container-'.$planid.'");

</script>
';

Next: TRY the button on your site!

If all goes well, the paypal payment popup opens and you can do the purchase with your sandbox buyer account.

geschlossen: Tutorial
von Kai
Avatar von

If you get the PHP error $userid not found, then comment it out for now!


You might also run into this error:

Error: Create Subscription Api response error: { "name": "RESOURCE_NOT_FOUND", "message": "The specified resource does not exist.", "debug_id": "51c5c788b893a", "details": [ { "issue": "INVALID_RESOURCE_ID", "description": "Requested resource ID was not found." } ], "links": [ { "href": "https://developer.paypal.com/docs/api/v1/billing/subscriptions#RESOURCE_NOT_FOUND", "rel": "information_link", "method": "GET" } ] }

Probably the problem is that you mix LIVE and Sandbox credentials (only use sandbox accounts and the Sandbox Client ID from the developer dashboard).

If you still getting "Bad Request", open up your browser and visit this URL to find the problem:
https://www.paypal.com/sdk/js?client-id=AYkqP... (enter your clientid)

----

In any case, with the code from the tutorial and triggering the webhook, open your server's error_log file and check if the webhook data is logged.

It should show something like this:

[26-Mar-2021 06:16:48 UTC] PAYPAL EVENT: BILLING.SUBSCRIPTION.CREATED
[26-Mar-2021 06:22:46 UTC] PAYPAL EVENT: BILLING.SUBSCRIPTION.ACTIVATED
[26-Mar-2021 06:22:46 UTC] I-D58CDVHRTBC0
[26-Mar-2021 06:22:46 UTC] userid: 68
[26-Mar-2021 06:23:17 UTC] PAYPAL EVENT: PAYMENT.SALE.COMPLETED
[26-Mar-2021 06:23:17 UTC] I-D58CDVHRTBC0
[26-Mar-2021 06:23:17 UTC] 3.00
[26-Mar-2021 06:23:17 UTC] completed
[26-Mar-2021 06:23:17 UTC] userid: 68

Or even more if you have logged the entire JSON object. :)

Also visit https://www.sandbox.paypal.com/billing/subscriptions you should see 1 active subscription now! :)

Part 1: Initial Paypal Subscription Button Setup, Paypal App Setup, Webhook PHP Script, Button on Sales Page
https://www.stacklounge.de/6325/tutorial-integrate-paypal-subscription-javascript-complete

Part 2: Check Customer Paypal Subscription status from our own Website, Go Live
https://www.stacklounge.de/6328/tutorial-integrate-paypal-subscription-javascript-complete

Part 3: Cancel the Subscription (Customer clicks "cancel" on your website)
https://www.stacklounge.de/6330/tutorial-integrate-paypal-subscription-javascript-complete

Ein anderes Problem?

Stell deine Frage

Willkommen bei der Stacklounge! Stell deine Frage einfach und kostenlos

x
Made by a lovely community