Post Page Advertisement [Top]


It's been quite some time since I write anything, I simply been too busy with my startup to find any time for writing. No more excuses, as today I will write about a subject that I needed to implement myself and that found difficult at first. So if you're like me and never heard about things like "payment gateways", "marketplace", "Stripe", etc, and want to learn more, keep reading!


Yeah, one day...




Payment gateways

If you're like me and never had to work on an e-commerce site before, or anything related with accepting payments online, you probably never bothered about what a payment gateway really is. Most of us think about PayPal when you heard those words, but its way much more complex than that. PayPal is just another player in the myriad of possible options out there. That said, in order to clarify concepts I recommended the reading of this nice resource, this truly helped me.

There are several things to consider when selecting a payment gateway: currency support, integrated checkout, refund policy, etc. If you plan to build a simple e-commerce site for you country/region, you can probably go straight with PayPal, Stripe or Braintree. They are all great and very straight forward in this case.

If you have more complex scenarios, like I did, for instance building a marketplace, where you charge on behalf of others (you charge the customers of your customers) then things get a bit more complicated. If that's your case, you can have a lot of good insights here, and also from shopify, which is by the way, a very well known marketplace. 

So let's build an app in Python-Flask to test how Stripe Connect works!

Stripe Connect

Stripe Connect is a subset from the Stripe ecosystem that allows you to basically charge to customers on behalf of other Stripe accounts, and getting a fee on the process (if you want). So basically, your marketplace users will end up having an Stripe account and you will use Connect to charge to their customers on their behalf. 


There a few things to consider though, you can opt to create a standalone or managed account. In my case, in Spain, there is no managed account so the decision was clear, but in case you have the possibility, I'll explain the difference. With a standalone account, your marketplace user will need to go to Stripe, create an account, will have access to the Stripe Dashboard and eventually they will be liable of any refunding or disputes with their Customers. A managed account, on the other hand, allows you to have finer grained control of what's going on. Marketplace users won't even notice that they have to use Stripe, you'll deal with everything. On the other hand, you will be responsible for any refunds in this case.


As I said, we wanted something simple at first and specially, not having to deal with refunds and disputes in our side, that why we chose also the standalone account. Let's get to it and see how to get a very simple test app with Stripe Connect, this way you will understand how things work. I've used Stripe in the past and for simple e-commerce sites in no-brainer, but with Connect you must understand a few concepts and might be problematic.



Creating a Stripe Connect account

First thing is going here and register your Stripe Connect platform. Here you have to choose between Standalone or Managed accounts. In my case, because of the reasons mentioned above, I've selected Standalone.



Account selection



You will need to specify the name, website URL, icon, etc and the most important part here is the Redirect URI, which is the endpoint in your app that will be used to handle the callback Oauth mechanism. You can either provide a real URI endpoint here (deploy your server app somewhere so it is accessible by Stripe) or you can use something like ngrok to test locally. This is what we're gonna do here.

Required fields


The server application

Let's create a server application to test all this. We're gonna be using Python Flask framework for this (mostly because its super easy to have a server app up&running and because I love Python :D).

I've already created a test project that you can play with so you don't need to start from scratch. So let's begin.

  • Clone the repo:
git clone https://github.com/internetmosquito/stripe_connect_test.git 
  • Create a virtualenv (optional) and install requirements.
    virtualenv stripe_test && source stripe_test/bin/activate && pip install requirements.txt
  • Create the local db used to store Customers (SQLlite)
    python create_db.py
     
  • Modify the _config.py file so you specify the keys for your Stripe platform account (the one you just created before).
    • API_KEY -> Copy this from the API Keys Tab in the Stripe Dashboard.
 
API_KEY

    • CLIENT_ID -> Copy this from the Connect Tab in the Stripe Dashboard.
 
CLIENT_ID

    • PUBLIC_KEY -> Copy this from the API Keys Tab in the Stripe Dashboard.
PUBLIC KEY
  • Next, you need to specify what is going to be the callback URL in your server. If you use my sample app should be somehting like {your_local_server_address}/oauth/callback. This is required to complete the typical "oauth dance". If you ever used Twitter, Facebook APIs, you should be familiar with this concept. Thus, go back to your dashboard and specify what this address should be. You have two options here, either you can use ngrok to tunnel any call from Stripe to your endpoint through the URI they give you, or you can use simply http://localhost:5000. I haven't tried the second approach, but some people say it works. I will explain how to use ngrok, which can help you in the future with other projects.
    • ngrok: Ngrok is a very nifty utility that does many things, but the thing that most interest us right now is that is able to tunnel any server you have running in your dev machine (thus, typically with a localhost or 127.0.0.1 address) to a real server address they provide randomly. That said, you can either download the client from their site, or you can install the official package with apt-get. I opted for the first option. Then simply unzip the file, and execute the binary directly. For our need, let's make ngrok tunnel all http and https traffic to our local server that is listening on port 5000 (the default port for our Flask app)
           ./ngrok http 5000

                You should get an output similar to this, although the URL generated obviously is different.

           ngrok by @inconshreveable                                                                                                             (Ctrl+C to quit)
                                                                                                                                                      
           Tunnel Status                 online                                                                                                                  
           Update                        update available (version 2.0.24, Ctrl-U to update)                                                                     
           Version                       2.0.22/2.0.20                                                                                                           
           Region                        United States (us)                                                                                                      
           Web Interface                 http://127.0.0.1:4040                                                                                                   
           Forwarding                    http://7bc5adb0.ngrok.io -> localhost:5000                                                                              
           Forwarding                    https://7bc5adb0.ngrok.io -> localhost:5000                                                                             
                                                                                                                                                      
           Connections                   ttl     opn     rt1     rt5     p50     p90                                                                             
                                         0       0       0.00    0.00    0.00    0.00 

               Copy and paste the http:/{whatever}.ngrok.io to Stripe dashboard.

Redirect URI
  • Now I'd recommend that you create a new Stripe account, an account that can "allow" our platform account (the one we just created above) to act on its behave, being able to create charges and transfers. You can skip if you want, and Stripe will ask you to create an account when authorizing the platform account app, but I recon is better if you do this now.
  • Fire up the server, we should be ready to test the app. Activate your virtualenv first (if you're using one).
      python stripe_connect_test.py

  • Open up a browser, go to http://127.0.0.1:5000/, you should get an output like this:
Home page
       First step is to authorize our app so we can act on behalf of another Stripe account, thus, click the "Connect with Stripe button". If you didn't create an account earlier as suggested, you will probably see a screen like this (in my case I named my test app Bloowatch). 

Authorizing unsigned


       You will have the option to create a Stripe account anyway here, so if you haven't done that yet, do it now. If, in the other hand, you created an account and are already signed in (I recommend having different browsers for this, one for your platform account - firefox maybe - and another for your connected account - chrome) you should get something like this (using my test connected account here)

Authorizing signed


       That said, if you are using your development and test keys (as suggested previously), you should get this top header notifying that you can skip this form by clicking the provided link, still, you will need a Stripe account to actually authorize the app.


Skip form

      Upon successful authorization, we retrieve the token and store it at the session level, this way you don't have to authorize again. 

    # Grab access_token (use this as your user's API key)
    token = resp.json().get('access_token')

    # We should store access_token, refresh_token, stripe_user_id,
    # For this demo, just save it in session for this demo
    session['access_token'] = token
    session['refresh_token'] = resp.json().get('refresh_token')
    session['connected_user_id'] = resp.json().get('stripe_user_id')

     This way we can easily check if we're authorized already. You should use a more convenient way to store access tokens if you plan to use them more than at a session level. Anyway, if everything goes well, you should see a page like this:

Successful auth

      I've added a Pay with Card button to create a test charge for our connected account. This will open Stripe Checkout Form, where a Customer of our connected Stripe account will be able to pay using fake data. So let's click the button and insert any email valid fake address, a Stripe testing card number (4242 4242 4242 4242), any date in the future and any 3 digit number as CCV. Keep in mind that we're gonna use this data to create a Customer for the connected and for the platform account (more on this later). Notice I'm using the simple form of this checkout, you can use a custom one where you can change the overall aspect of this form, among other things.

Stripe Checkout form
        After paying with the customer, Stripe returns a token that contains the authorization to proceed with the actual payment. This has several major implications:

  • Stripe will not make the charge instantly. It is up to you (the platform account) to decide when to make it, keeping into account that the provided token expires after 7 days (more on this later)
  • Stripe holds the credit card data, so you don't have to store it in your servers. This is great if you don't want to bother being compliant with PCI, which can be quite a pain.
  • If your customer click the checkbox to store credentials, Stripe will store this info in case the customer ever comes back so there is no need to re-introduce credit card information again.
        One of the great features of Stripe is the concept of Customers. You can have customers both in the platform and in the connected account, sharing those between the two. This is what we're gonna do here. We're storing customers also in a SQLlite db locally for convenience, but you can skip this process. Thus, after getting the token from the checkout form, check if the provided email is not from an existing customer in the platform account, if there was, no need to create a new Customer, otherwise we do, and store it in our local db:

       
      # Check if there is a Customer already created for this email
      platform_account_customers = stripe.Customer.list()
      platform_customer = [cus for cus in platform_account_customers if cus.email == stripe_email]
      # If there was no customer, need to create a new platform customer
      if not platform_customer:
          stripe_customer = stripe.Customer.create(
              email=stripe_email,
              source=stripe_token,
          )
          # Check if we had the customer in he db
          if not db.session.query(models.Customer).filter('email' == stripe_email).count():
              #Create the db user
              new_customer = models.Customer(
                  stripe_id=stripe_customer.stripe_id,
                  email=stripe_customer.email,
                  account_balance=stripe_customer.account_balance,
                  creation_time=stripe_customer.created,
                  currency=stripe_customer.currency,
                  delinquent=stripe_customer.delinquent,
                  description=stripe_customer.description,
              )
              db.session.add(new_customer)
              db.session.commit()

         Because we have used the token obtained from the checkout form to create this customer instead of using it to create the Charge, we have "spent" it (you can only use this token once). Thus, we need to re-create this token in order to share the customer between the platform and the connected account.  

       # Need to recreate the token to be able to crete the customer on the connected account too
       cus_token = stripe.Token.create(
           customer=stripe_customer.id,
           stripe_account=connected_user_id
       )
       # Create the customer in the connected account
       connected_account_customer = stripe.Customer.create(
           email=stripe_customer.email,
           source=cus_token.id,
           stripe_account=connected_user_id,
       )

         Now we have the same Customer both in the platform and in the connected account. We can proceed with the Charge now. There are basically two ways you can charge, directly or through the platform. Choosing one or another (I've chosen directly for this demo), has several implications, so you need to take your time before choosing, but basically summarizes to:
  • If you created a managed account instead of a stand-alone account, it is better to charge through the platform
  • If you need finer grained control over fees and information displayed on credit card charges, charge through the platform
  • The platform account pays the Stripe fee.
  • The destination account (normally the connected account) is attributed with the charge, this is important for tax reporting.
        Thus, if you need finer grained control, you better charge through the platform. In my case, charging directly is good enough. That said, let's create the Charge for the previously created customer in the connected account (that automatically grants the charge will be transferred to the connected account)

      # Make the charge to the customer on the connected account
      amount = 10000
      stripe.Charge.create(
          amount=amount,
          currency='eur',
          customer=connected_account_customer.id,
          stripe_account=connected_user_id,
          application_fee=123,
      )

        Notice the usage of customer, this makes the Charge to be done using the Customer instead of the stripe token returned from the Checkout Form. This is essential if you plan (like I do) to charge the Customer way after the 7 day period you have with this token. 

        Thus, after successful payment, we will have a new Customer created in the platform account and the connected account. If everything goes well you should see this page.

Success charge
      In the Platform account, you should see the newly created Customer:

Platform customer


      You should see the fee from the payment made before:


Platform fee
       In the other hand, in the connected account, you can see the Customer too:

Connected account customer

       And also the payment made by the "customer" before:

Connected account payment


Final words

As you can see, using Stripe is quite easy and straight-forward, this is specially true if you plan to use it with a marketplace. Sure you can discard this and start implementing you're own payment gateway, but this will take A LONG TIME, and if you're building your start-up, time is not something you normally have plenty to spare. After all, the business model from Stripe is fair, if you make money, they make money, simple as that. Its a very good alternative to get you started in the payment gateway ecosystem out there. Enjoy!




13 comments:

  1. Ha ha ha ha was a appealing title indeed! Great Post!

    ReplyDelete
  2. Hi,
    I'm trying to create a multivendor marketplace where the payment is direct to the vendor (all tax and refunds handled by the vendor, not the marketplace.) But it can't be done with Stripe Connect because PCI compliance requires a single direct transaction per payment to the vendor. I haven't found an ecommerce platform that can handle charging the customer in split transactions. So if a cart contains a $20 product by one vendor and a $10 product by anothet, the customer checks out once but there are 2 card transactions. Do you have any suggestions? Thankd

    ReplyDelete
  3. Have you looked at Braintree? I think they had something implemented on this regard, having several transactions in a single order for different vendors, but maybe it was just me that understood this wrong...

    ReplyDelete
  4. If you want to have more than one other connection, you will need to have a network switch or other network access point.
    visit chrismartslaw.com

    ReplyDelete
  5. I should say only that its awesome! The blog is informational and always produce amazing things.
    mason soiza

    ReplyDelete
  6. All good. I am a newcomer, so I jerked until the money was withdrawn. I waited 2 days on Yandex. Thanks to the admins and support service for listening to my whining on the forum and in the chat. Play people! Do not be greedy. All the rules with this casino! perfectbest online slots I often spend my time here

    ReplyDelete
  7. Hey Alejandro,

    Thanks for such a descriptive blog and the repo. I was going through the Stripe docs and was a little lost. Came across this page of yours and it has clarified a lot of doubts I had in mind. Will surely follow the steps you have laid out.

    Best Regards,
    Rishi

    ReplyDelete
  8. It has fully emerged to crown Singapore's southern shores and undoubtedly placed her on the global map of residential landmarks. I still scored the more points than I ever have in a season for GS. I think you would be hard pressed to find somebody with the same consistency I have had over the years so I am happy with that. Customers to your website

    ReplyDelete
  9. Really nice and interesting post. I was looking for this kind of information and enjoyed reading this one. Keep posting. Thanks for sharing. berita artis korea terbaru hari ini

    ReplyDelete
  10. Really nice and interesting post. I was looking for this kind of information and enjoyed reading this one. Keep posting. Thanks for sharing. visit bali

    ReplyDelete
  11. There are various definitions from various associations (basically APICS) however these are the ones I will in general see and my interpretation of how great Business Central for assembling is intended for these modes. informative post

    ReplyDelete
  12. You are giving such interesting information. It is great and beneficial info for us, I really enjoyed reading it. Thankful to you for sharing an article like this.Self-Ordering Kiosk System Solutions Singapore

    ReplyDelete

Bottom Ad [Post Page]