With the release of the subscription (recurring payment) APIs from PayPal, it is possible to create your anything-as-a-service buisness faster than ever. SDK integrations provide a native way to integrate with the PayPal APIs in the language of your choice. In this tutorial, we will walk through a sample application built using the PayPal Python sdk, the microframework Flask, Bootstrap for UI and Heroku for deployment.
If you have not already, get your credentials from PayPal Developer Portal and see the Subscription api docs. Afterwards, set up the Python sdk. It may also be worth checking out Flask to see how to set up a simple web application (hello world is only 7 lines long!).
As a merchant/developer, you most common use cases may be view the billing plans you have created already. For example, to view all created plans in descending order you could do
# Result is a Json object with an array of plan objects
plans_created = BillingPlan.all({"status": "CREATED", "sort_order": "DESC"})
To display the plans in a html page, you can use your preferred templating engine, here we use Jinja. We are using bootstrap frameworks grid system, by using the col-lg-7 tag to ensure that the columns line up properly on the html list.
{% for plan in plans_created %}
<li class="list-group-item">
<div class="row">
<div class="col-lg-3 col-md-3 text-center">
</div>
<div class="col-lg-7 col-md-7 section-box">
<h2>{{ plan.name }}</h2>
<p>{{ plan.description }}</p>
</div>
<div class="col-lg-2 col-md-2 section-box">
<form action="{{ url_for('activate', id=plan.id) }}" method="post">
<input type="hidden" id="activate" name="activate" value="activate" />
<button class="btn btn-success btn-md" type="submit"><span class="glyphicon glyphicon-cloud-upload"></span> Activate</button>
</form>
</div>
</div>
</li>
{% endfor %}
You can set up your necessary parameters for creating a Billing Plan. After creating, you may want to change to status of the plan to Active so that a customer can subscribe for the plan. In the app, the activate button sends a post request to the activate endpoint, appending the plan id as a parameter.
<form action="{{ url_for('activate', id=plan.id) }}" method="post">
<input type="hidden" id="activate" name="activate" value="activate" />
<button class="btn btn-success btn-md" type="submit"><span class="glyphicon glyphicon-cloud-upload"></span> Activate</button>
</form>
The http post call made on the activate endpoint is handled by the activate method which calls billing_plan.replace with state set to ACTIVE.
@app.route("/activate", methods=['POST'])
def activate():
if session.get('logged_in') and session.get('merchant'):
billing_plan_update_attributes = [
{
"op": "replace",
"path": "/",
"value": {
"state": "ACTIVE"
}
}
]
billing_plan = BillingPlan.find(request.args.get('id', ''))
print("Billing Plan state is %s " % (billing_plan.state))
if billing_plan.replace(billing_plan_update_attributes):
billing_plan = BillingPlan.find(request.args.get('id', ''))
print("Billing Plan state is %s " % (billing_plan.state))
else:
print billing_plan.error
return redirect(url_for('admin'))
else:
return redirect(url_for('login'))
Now your users can subscribe for the plan. In this app, on login the user sees the active plans on login with a form button that makes a post to the subscribe endpoint of the application with the plan id as parameter.
<form action="{{ url_for('subscribe', id=plan.id) }}" method="post">
<input type="hidden" id="subscribe" name="subscribe" value="subscribe" />
<button class="btn btn-success btn-md" type="submit"><span class="glyphicon glyphicon-shopping-cart"></span> Subscribe</button>
</form>
The subscribe endpoint introduces the second key component of subscription apis, the Billing Agreement. The Billing Plans act as a template for the Billing Agreements which you can have users subscribe to with payment method, shipping address and other customer specific information.
We instantiate the BillingAgreement class with the necessary attributes. Afterwards we call create() on billing_agreement to create the agreement. In this example, the payment_method is paypal.
The next step of the process is to redirect the user to paypal for approving the payment. The billing_agreement created returns a list of HATEOS links where the link with attribute rel set to approval_url is the url you want to redirect the user to.
@app.route("/subscribe", methods=['POST'])
def subscribe():
if session.get('logged_in') and session.get('customer'):
billing_agreement = BillingAgreement({
"description": "Agreement for organization plan",
"start_date": "2015-02-19T00:37:04Z",
"plan": {
"id": request.args.get('id', '')
},
"payer": {
"payment_method": "paypal"
},
"shipping_address": {
"line1": "StayBr111idge Suites",
"line2": "Cro12ok Street",
"city": "San Jose",
"state": "CA",
"postal_code": "95112",
"country_code": "US"
}
})
if billing_agreement.create():
print("Billing Agreement created successfully")
for link in billing_agreement.links:
if link.rel == "approval_url":
approval_url = link.href
return redirect(approval_url)
else:
print(billing_agreement.error)
return redirect(url_for('subscriptions'))
else:
return redirect(url_for('login'))
After the user has approved the subscription, they would then the redirected to the return_url you set up while creating the billing plan. For this application, that is the execute endpoint of the application. PayPal appends a payment token to your return_url as a parameter which you can pass into the BillingAgreement.execute method of the sdk to execute the agreement.
@app.route("/execute")
def execute():
payment_token = request.args.get('token', '')
billing_agreement_response = BillingAgreement.execute(payment_token)
print("BillingAgreement[%s] executed successfully" % (billing_agreement_response.id))
return redirect(url_for('agreement_details', id=billing_agreement_response.id))
That is the full flow of creating a billing plan, activating and having a user subscribe to it! You may want to display the details of the created plan to the user, done via calling the find method of the BillingAgreement class. A very common use case would be for a user to see their payment history which can be done via calling search_transactions on the agreement and passing in the start_date and end_date as requested.
billing_agreement = BillingAgreement.find(request.args.get('id', ''))
transactions = billing_agreement.search_transactions(start_date, end_date)
To view what else is possible with billing agreements, check out the API specs.
Creating an agreement with credit_card as the payment method is also possible similar to creating a payment.
Where to find on github and where to find on heroku
When your next killer app needs a
We have conveniently used PayPal as our backend here We use PayPal as the backend and no database is required