Integrating PayPal Checkout with Anvil

Hello,

I’m currently trying to integrate PayPal into my Anvil app. I have a custom HTML file that contains the PayPal code and it is being used within a form (form2) that holds the PayPal code and other elements like buttons. My app has a startup form (form1) that is separate from form2.

I’m trying to call a server function from inside the custom HTML of form2, specifically within the PayPal code, so that I can flag a user as paid after a successful transaction. However, I’m receiving a script error when I try to do this. I’ve included the code I’m using within the onApprove function below:

onApprove: function(data, actions) {
                return actions.order.capture().then(function(orderData) {
                    // Successful capture! For demo purposes:

                    console.log('Capture result', orderData, JSON.stringify(orderData, null, 2));
                    var transaction = orderData.purchase_units[0].payments.captures[0];
                    alert('Transaction '+ transaction.status + ': ' + transaction.id + '\n\nSee console for all available details');
                    if (anvil.users.logged_in()){
                        var user = anvil.users.get_user();
                        anvil.server.call('mark_user_as_paid', 
                    {order_id: data.orderID, user_email: user["email"]})
                    }
                });
            }


This is the server function:

@anvil.server.callable
def mark_user_as_paid(order_id, user_email):

    user = anvil.users.get_user()
    if user == None:
      user = anvil.users.login_with_form()

    else:
        user_row = app_tables.users.get(uuid=user_email)
        user_row['paid'] = True
        user_row.update()
        print("Transaction successful")

I’m sorry if my questions aren’t making sense or if I haven’t provided enough details. I’m a bit lost right now and would really appreciate the help.

Welcome to the forum!

There is a way to call a Python function from Javascript, but I don’t use it and can never find it in the docs when I want to look it up. Hopefully someone else who uses it can point you to the docs for it.

An alternative to that is to have the onApprove callback be a Python function itself. I don’t know how the rest of the PayPal code works, so can’t offer any concrete advice on making that happen. In general, though, you can access, through Python, any Javascript objects that have been created, and set callbacks to Python functions. You generally never need to write actual Javascript: Anvil Docs | Accessing JavaScript

I don’t use this either, but here’s the docs: https://anvil.works/docs/client/javascript/html-forms#calling-python-from-javascript

Thanks for the response

Just for reference, I got the PayPal code from here: https://developer.paypal.com/integration-builder/

Thank again, I’ll check out the docs regarding your suggestion :grinning:

Do note that in some situations in Javascript you can’t call a function that will take time (like a server call). If you hit one of those situations the error will be something like “Cannot call blocking function here”.

Took a look at the builder…this may be more work than you want to get into, but everything that is in the script tags can be done in Python using the Javascript bridge. The Javascript bridge has matured to the point where you don’t really need to write any Javascript. Each of the callback functions would be actual Python functions. I find it easier to work in Python than in Javascript, but I’ve gone through the learning curve of working with the Javascript bridge.

Edit: here’s an example from integrating CodeMirror Javascript into an Anvil form. This code is in the form_show handler of an Anvil form:

    from anvil.js.window import CodeMirror

    self.cm_editor = CodeMirror(self.add_editor, {
      'lineNumbers': True,
      'toolBar': True,
      'mode': 'text/html',
      'configureMouse': self.configureMouse,
      'lineWrapping': True,
    })

Everything declared in Javascript becomes available in the anvil.js.window scope, so can be imported into Python. Instead of a Javascript object being passed in as the second parameter, you use a Python dictionary. The configureMouse callback will call a function on the form.

You should be able to do something similar with the Paypal script bits, e.g. (totally untested):

    from anvil.js.window import paypal.Buttons

    button = paypal.Buttons({
       'style': {style stuff here},
       'createOrder': self.createOrder,
       etc.

Then the callbacks would take the same arguments shown in the Javascript, but be Python functions on the same form:

    def createOrder(self, data, actions):
       etc

1 Like

Just want to chime in here and say if you like Javascript, by all means use it if you wish, but if you don’t want to use javascript, paypal also has a REST API service available. At the last place I worked, we interacted with it directly on the server using PHP, so directly consuming the API is pretty much language agnostic.

Also, anvil has many many built in ways to interact with API’s and HTTP endpoints already, from within python.

1 Like

Hi, I tried following the suggestion in the Anvil docs to call a Python function from JavaScript, but I’m having trouble determining what the first argument should be. According to the docs, the first argument should be a DOM element that indicates which form instance to call the function on, but I’m not sure how to determine what that should be. Could you please help me understand how to determine what the first argument(‘.content’) should be in this scenario? Thank you.

Sorry, I’m really not that familiar with javascript :smiling_face_with_tear:

// (in JavaScript:) 
function myFunc() {
    anvil.call($('.content'), 'my_method', 'world')
}

console error:

Cannot call anvil function on HTML Panel: (2)
S [] (0)
"Did you forget to supply 'this' as the first argument to anvil.call?"

@jshaffstall @hugetim

For starters, you can’t call a server function directly from JavaScript. You’ll need to create a client-side method within form2 which can then in turn calls the server function. From javascript, you will then call that form2 method, and then you should be able to just use this as the first argument to anvil.call, as the error message suggests.

If this is the case I strongly suggest you check out the paypal docs for connecting through the API

Above is the response from @meredydd back in the day about Paypal integration, it was also the suggested method of integration.

Anvil is already very good at this and already built for using APIs, when implementing it from scratch is usually very hard.

Just glance it over and decide if you really want to keep trying the javascript route.

Also see:

3 Likes

I get the idea of using the Paypal API
But (if I´m right) you need to use an anvil button that will not look like a Paypal button.

The first javascript code is propossed by Paypal to add the payments button (with the proper look and feel).
I asked ChatGPT how store the payment result on the client-python-side for the same javacript code (approved/rejected) in order to lead the user to the next form (product download)
I got this
onApprove: function(data, actions) {
return actions.order.capture().then(function(orderData) {
// Successful capture! For demo purposes:
console.log(‘Capture result’, orderData, JSON.stringify(orderData, null, 2));
var transaction = orderData.purchase_units[0].payments.captures[0];
anvil.call(‘transaction_function’, transaction.status);

Where the “transaction_function” exists on the client-side.
However, nothing happens when I added this. Is this correct or a maybe a tweak in the code is needed?

Regards

Just in regards to the button, you can make any button look any way you like, but yes, the javascript one will auto-populate the newest version of the paypal button.

Here is the current checkout buttons from the cart of the place I used to work, the assets are imbedded in the server, and not retrieved on the fly:

There is nothing wrong with this, but the api can have more configuration to achieve the same exact ‘look and feel’ of the javascript drop-in, yes.

A post was split to a new topic: How to capture microphone data