Simple PayPal 'Checkout'

Hi All,

Working on my open source community platform and just finished making my PayPal integration work. It was fairly straightforward once I went through the PayPal API docs. I didn’t care to make the button look like the ‘real’ button so I just used a regular Anvil button.

The API works like so:

  1. User needs to call the Subscriptions API with a Plan ID to create a URL to the plan’s checkout page. That API returns the URL as well as a new subscription ID.
  2. You need to point it to an API endpoint as the return_url and cancel_url to capture whether the payment was successful or if the user canceled the checkout.
  3. You now have the subscription ID in the Users table which you can use to manage or check up on a user’s subscription status.

Anyway, here’s some client code for a button:

    def btn_pay_new_click(self, **event_args):
        """This method is called when the button is clicked"""
        from anvil.js import window
        self.user['paypal_sub_id'], self.payment_url = anvil.server.call(
            'create_sub', self.user['fee']
        )
        self.btn_save_click()
        window.open(self.payment_url)
        self.refresh_data_bindings()

And server code:

@anvil.server.callable(require_user=True)
def create_sub(plan_amt):
    import requests
    access_token = get_paypal_auth()
    print(anvil.server.get_app_origin())

    if plan_amt == 10:
        plan_id = anvil.secrets.get_secret('pp_plan_id_10')
    else:
        plan_id = anvil.secrets.get_secret('pp_plan_id_50')

    try:
        response = anvil.http.request(
            'https://api.paypal.com/v1/billing/subscriptions',
            method='POST',
            headers={
                'Authorization': f'Bearer {access_token}',
                'Content-Type': 'application/json'
            },
            data={
                'plan_id': plan_id,
                'application_context': {
                    'return_url': anvil.server.get_api_origin() + '/capture-sub',
                    'cancel_url': anvil.server.get_api_origin() + '/cancel-sub'
                }
            },
            json=True
        )
        return response['id'], response['links'][0]['href']
    except anvil.http.HttpError as e:
        print(f"Error {e.status} {e.content}")


@anvil.server.http_endpoint('/capture-sub')
def capture_sub(**params):
    row = app_tables.users.get(paypal_sub_id=params['subscription_id'])
    row['good_standing'] = True
    return 'Payment success! You can close this tab.'


@anvil.server.http_endpoint('/cancel-sub')
def cancel_sub(**params):
    row = app_tables.users.get(paypal_sub_id=params['subscription_id'])
    row['paypal_sub_id'] = None
    return 'You have cancelled enrollment. You can close this tab.'

Check out our GitHub repo:

8 Likes