PhonePe's Payment Gateway x Flutter | A rant about Payment Gateway Integration Horrors
Introduction
A story about how I setup PhonePe for my client's Flutter application, how it took 2 months of blood, sweat, tears and curse words.
Integration Timeline
I recently had a client switch payment gateway providers since they lost access to their CashFree integration. Since most providers haven't got their full Payment Aggregator license, they can't onboard. At the same time my client also got kicked off the Play Store due to a issue with their Privacy Policy). Long story short they decided to setup a new everything with a new company requiring us to setup a new payment gateway. TLDR my client's app got deleted completely and needed a new payment gateway.
This issue started about 60 days ago, the only payment gateway my client could onboard onto was PhonePe's Flutter Payment Gateway. We saw in their docs they had a flutter page so we gave the green light that we'd integrate in no time.
If you want to read about my setup with PhonePe skip to How I Setup PhonePe, and How the Flutter Payment Gateway SDK Works.
I won't disclose the name of my PhonePe integration support engineer, or their merchant support but to say the least it's the worst company I've ever interacted with.
The Email I Sent to PhonePe
Hello PhonePe integration team, for starters your knowledge of your own merchant is horrid and unacceptable. Beyond I can see why the documentation is so poor and even with Payment Gateway's in India being nearly unavailable no one opts for your worst than no payment gateway service.
Again, we're using a Flutter application for our business needs and are using your documentation and official Flutter package for this. We've attached the base64 request, the checksum and screenshots of our code alongside this email.
Our testing environment is using Google's Internal App Testing system which uses the same signing release key as our production release will and the same place we shared the signature through with you. We also use Google Play Signing for signing our apps before release.
Please UNDERSTAND OUR LINE OF WORK DOES NOT NEED A RETURN/ SHIPPING POLICY AND WE'VE TIME AND TIME AGAIN SHARED A REFUND POLICY
Our package id is "com..*******" and our playstore url is "https://play.google.com/store/apps/details?id=com..". Please make sure you whitelist this as it is the bare minimum you can do.*
If you have any doubts or a lack of understanding of how your system works let us know and we'll help you hire people, if you already have technical people I'll be glad to speak to someone with any level of awareness whatsoever of what the issue is. Please refer to the screenshots attached on how we're using your package and system.
Here's a Sequence of Events Prior to This Email
- Client decides to onboard with PhonePe, talks to PhonePe support and gets us onboarded.
- PhonePe takes 4-5 takes to take a package id from us, and kept complaining about how we had no app on the play store. Eventually I gave up and created a PlayStore url with the app id since I know the structure.
- PhonePe took another week and a half to give us a merchant id and a salt key, at this point we started setting up our UAT environment. The PhonePe docs are fucking awful, incomplete and vile.
- In our UAT environment we ran into way too many issues with the Flutter SDK and took nearly two weeks to set this up.
- After this we decided to take our system to production and requested a salt key. This took 4 days but made no fucking sense, they never tested our app or discussed our implementation, if they didn't need to do all this why not provide us with it at the time of onboarding?
- After this we finally integrated the SDK and everything worked flawlessly, but to our displeasure the payment's never worked, since there a was a security block, because they never whitelisted our package id that they asked for in the second step. Upon sending a picture of my other phone via my main phone the integration engineer said and I quote "The request should be made from same domain, not localhost". After this comment I decided to write a softly worded email to PhonePe about the issue and they said "You don't have a shipping and return policy". Which is a valid response often but not when you're selling subscription services. We responded and they just asked for the same thing fucking again, the retards.
- This issue took two weeks to solve, I sent the email at the start of the two weeks. Ironically PhonePe never responded to that email, still haven't.
- One morning I woke up and my client's PhonePe finally worked, and I an text from the support engineer "No other merchants have faced this much delay except 'Wrong company name'". Indeed a ironic way to end our last interaction.
Of course, quite a few interactions, delays, galis are missing from this description but even as is, it's a horror story for any engineer. Across the 60 days PhonePe didn't work on weekends, didn't respond on holidays, replied to emails without reading them of course lol, and the integration engineer rarely picked up his phone. But eventually by hook or by crook it all worked out, my client is okay for now, of course they're never happy. Now that you know everything here's the technical part of how it all works, the unofficial but better PhonePe docs for flutter.
How I Setup PhonePe, and How the Flutter Payment Gateway SDK Works
Terminology
- Merchant Id- Essentially your id as a merchant, so PhonePe knows who gets the small bucks. [ Essentially Public ]
- App Id- Essentially tied to the SHA-1 of your release build, publish to internal app testing and call the method from the PhonePe SDK to get this. [ Essentially Public ]
- Salt Key- A way for PhonePe to authenticate the requests comes from you, explained in more detail below. [ Confidential ]
- Checksum- A authentication token of sorts, unique to each transaction that is created by clubbing transaction details and your salt key. [ Public ]
1. Initialise the SDK
This image is from the production environment, if you're in UAT or UAT SIM you can leave "app id" as an empty string when initialising. The merchant id should be "PGTESTPAYUAT" if you're testing out the SDK in UAT. Only use your own merchant id in production, if you're in UAT also use the "UAT" or "UAT_SIM" environment to initialise the SDK and please await the response and log it so you don't go insane if it fails.
In UAT or UAT_SIM, only use the creds from here "https://developer.phonepe.com/v1/docs/uat-testing".
This image shows the initialization of the PhonePe SDK in a production environment.
2. Create the Request Body and the Checksum
Here's the request body, I use for most of my transactions:
self.order_id = "MT" + str(random.randint(100000000000000, 999999999999999))
self.request_body = json.dumps({
"merchantId": settings.PHONEPE_MERCHANT_ID,
"merchantTransactionId": self.order_id,
# create the user id based on the user id + phone
"merchantUserId": "MUID" + str(self.user.id) + str(self.user.phone),
"amount": int(self.amount) * 100,
"redirectUrl": "https://example.com/redirect-url",
"redirectMode": "REDIRECT",
"callbackUrl": "https://example.com/callback-url",
"paymentInstrument": {
"type": "PAY_PAGE",
}
})
Now to convert that to a SHA256 for the X-VERIFY header here's what I did.
# convert body to base 64
self.request_body_base64 = base64.b64encode(str(self.request_body).encode('utf-8')).decode("utf-8")
# create sha256 checksum
self.checksum = sha256(base64.b64encode(str(self.request_body).encode('utf-8')) + str("/pg/v1/pay").encode('utf-8') + str(settings.PHONEPE_SALT).encode('utf-8')).hexdigest() + f"###{settings.PHONEPE_SALT_INDEX}"
This should only be done on the backend, but why? Because this involves the use of the salt, the salt is a stupid but good enough way for PhonePe to authenticate an API call from you.
Payment Process Overview
- Understand the payment details, i.e amount, user id, transaction id and everything else.
- Take it and translate it into base 64 of data + /pg/v1/pay + checksum, now you take this and run a SHA256 hashing algorithm on it and it spits out a hash, you append your salt key id to the end of this such as ###1. This is your checksum.
- The request to PhonePe will have two components the base64 of all the transaction details and the checksum. The transaction goes in the body and the checksum as X-VERIFY header in the request.
- PhonePe will respond to all this crap with a payment link that you can go to and then based on success or failure you'll get redirected to the call back you give it.
Start the Transaction
I summarised this into a simple function into my flutter app called postTransaction
Creating the transaction
4. Start the transaction
Starting the transaction
Pass in the transaction to the SDK and await the response. Add print statement across the board so you know what's happening. The construction of the request should happen on your backend in the production environment.
5. Check the Status Of The Transaction
Checking the transaction status
Check the status of the transaction, upon the success of the transaction just show a success and refresh the data in your app. Always provide the benefits of the transaction upon the success of it via the Backend. Never expose an API just for this purpose unless you know what you're doing.
Here's What My Transaction Model Looked Like
Transaction Model
The main items in here were request body, request body base64 and the checksum. The rest were random testing variables and so on.
Backend Validation
On the Django backend, I used the PhonePe python sdk, do note that you will need to get your public IP address whitelisted by PhonePe for this purpose otherwise their api's will not be happy to receive your API call.
I get a success or failure status from the API response and also load in information such as method of payment and some other stuff, since my client asked for this information specifically.
Tips and Tricks
- Rate limit your API's for creating transactions, I allowed upto 5 per day based on IP addresses.
- PhonePe can crap out at any time, my PhonePe engineer told send the app into production and it will work out, it did not. Be smart, setup a feature flag for your application.
- Don't ask PhonePe for help, they'll make it worse.
Conclusion
The PhonePe docs are not the worst thing in the world, but their support and engineers are. The PhonePe docs assume a great deal of knowledge about the PhonePe system and why something is done a certain way. The PhonePe engineers do not give a shit about your use case or know anything about your app. At the time of onboarding my engineer shared the app id of another client with me, he could not care less the about the merchant or the product they were aspiring to build.
If you can go without PhonePe then it's highly recommend, but if you don't have any other option and need help with your implementation, please read this blog thoroughly as the devil is in the details and reach out via LinkedIn if something does not pan out for you.
PS: PhonePe reached out to me after posting this blog, and after a 45 min call with their Director for Product Management and their Engineering Team I cannot still assure you that they're working on improving this but they did take a bunch feedback from me.