-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
article: guide to integrate mobile money payment in a web app #34
Open
abdounasser202
wants to merge
1
commit into
osscameroon:main
Choose a base branch
from
abdounasser202:mobile-money-integration
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
171 changes: 171 additions & 0 deletions
171
data/blog/mobile-money-integration-python-cameroon-2024.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -0,0 +1,171 @@ | ||||
--- | ||||
title: Guide to integrate mobile money payments for a Python web app with Notchpay in Cameroon | ||||
date: '2024-09-07' | ||||
tags: ['Payment API', 'Mobile Money', 'Python'] | ||||
draft: false | ||||
summary: 3 simple steps to integrate mobile money payment into your Python web application | ||||
images: [] | ||||
layout: PostLayout | ||||
canonicalUrl: mobile-money-integration-python-cameroon-2024 | ||||
authors: ['nab'] | ||||
--- | ||||
|
||||
This is a translation of an article I wrote in January 2024. In that article, i'm explaining [how to integrate Orange Money or MTN Momo in a python web application](https://peef.dev/post/nasser/comment-integrer-les-paiements-orange-money-ou-mtn-momo-au-cameroun-dans-son-application-avec-python-286). | ||||
|
||||
Just for you to know, i wrote that article because i wanted to share with every developer in Cameroon, the steps i did follow to integrate a mobile money payment provider to my [marketplace](https://peef.dev). I found that platform really simple, so, let's get started. | ||||
|
||||
[Notchpay](https://notchpay.co/) offers you a way to integrate payment methods into your application and supports card and mobile money payments. | ||||
|
||||
The integration is simple: | ||||
|
||||
First, create an account on [Notchpay](https://business.notchpay.co/). Your account gives you access to a sandbox that allows you to test the API. In the **settings** (Settings > Developer), you'll find your **API key** (PUBLIC_KEY) that you'll use for authentication in your application. | ||||
|
||||
## Initiating payment | ||||
|
||||
The [documentation](https://developer.notchpay.co/) already describe all the endpoints you can use, particularly in the [API reference](https://developer.notchpay.co/api/tag/payment) section. | ||||
|
||||
In our case, we will use [Flask](https://pypi.org/project/Flask/) to build the controllers and the [Requests](https://pypi.org/project/requests/) library to make requests to Notchpay. | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it makes sense to explain a bit why Flask is chosen. |
||||
|
||||
Let's first initialize the payment: | ||||
|
||||
```python | ||||
def initialize_payment( | ||||
email, currency, amount, phone, reference, description, callback | ||||
): | ||||
headers = {"Authorization": PUBLIC_KEY, "Accept": "application/json"} | ||||
url = "https://api.notchpay.co/payments/initialize" | ||||
data = { | ||||
"email": email, | ||||
"currency": currency, | ||||
"amount": amount, | ||||
"phone": phone, | ||||
"reference": reference, | ||||
"description": description, | ||||
"callback": callback, | ||||
} | ||||
|
||||
response = requests.post(url, headers=headers, data=data) | ||||
|
||||
if response.ok: | ||||
json_response = response.json() | ||||
return json_response | ||||
else: | ||||
return {"status_code": response.status_code, "msg": response.text} | ||||
``` | ||||
|
||||
In that function: | ||||
|
||||
1. We define the necessary data for the payment as arguments, in the **data** variable, | ||||
2. Then the **header** through which we execute our request. This header contains the API Key | ||||
3. And we return a JSON response. | ||||
|
||||
For more information on this payment initiation endpoint and the JSON response, just go to the documentation of that endpoint [/payments/initialize](https://developer.notchpay.co/api/tag/payment/post/payments) | ||||
|
||||
Additionally, note that the callback will be a controller on your site that will be called after the payment. You can use it to implement payment verification. | ||||
|
||||
- From there, we'll create our controllers: first a simple web page that displays a "Pay Now" link. | ||||
- When this link is clicked, the **pay()** controller is called and will initiate the payment then will do a redirection to the Notchpay payment page: | ||||
|
||||
```python | ||||
return redirect(init_payment.get("authorization_url")) | ||||
``` | ||||
|
||||
- At this point, the Notchpay page dedicated for the payment will be displayed | ||||
- The user can then make his payment via credit card or mobile money phone number | ||||
|
||||
```python | ||||
HTML_PAGE = """ | ||||
<h1>Process payment</h1> | ||||
<a href="/pay">Pay Now</a> | ||||
""" | ||||
|
||||
@app.route("/") | ||||
def home(): | ||||
return HTML_PAGE | ||||
|
||||
@app.route("/pay") | ||||
def pay(): | ||||
payment_reference = uuid.uuid1() | ||||
init_payment = initialize_payment( | ||||
email="[email protected]", | ||||
currency="XAF", | ||||
amount="1500", | ||||
phone=None, | ||||
reference=payment_reference, | ||||
description=f"Payment description {payment_reference}", | ||||
callback=f"http://localhost:5000/verify", # make sure to have the right host | ||||
) | ||||
return redirect(init_payment.get("authorization_url")) | ||||
``` | ||||
|
||||
When the payment is made by the user, it must then be verified through the callback that was passed to the **initialize_payment()** function. | ||||
|
||||
## Payment verification | ||||
|
||||
Here is the verification function : | ||||
|
||||
```python | ||||
def verify_payment(reference): | ||||
url = f"https://api.notchpay.co/payments/{reference}" | ||||
headers = {"Authorization": PUBLIC_KEY} | ||||
|
||||
response = requests.get(url, headers=headers) | ||||
|
||||
if response.ok: | ||||
json_response = response.json() | ||||
logger.debug(json_response) | ||||
return json_response | ||||
else: | ||||
return {"status_code": response.status_code, "msg": response.text} | ||||
``` | ||||
|
||||
1. This function takes as parameter the payment reference which is passed in the callback via a **GET** method from Notchpay. | ||||
2. We then construct the header through the URL [/payments/{reference}](https://developer.notchpay.co/reference/payments#verify-and-fetch-payment) for payment verification | ||||
3. and return a JSON response | ||||
|
||||
The callback will be the **verify()** controller which will extract the reference and pass this payment reference to the **verify_payment()** function | ||||
|
||||
```python | ||||
@app.route("/verify") | ||||
def verify(): | ||||
reference = request.args.get("reference") | ||||
return verify_payment(reference) | ||||
``` | ||||
|
||||
From there, you can just retrieve the JSON response to continue your process based on the response (payment failure or success) | ||||
|
||||
## Payment verification through a Webhook | ||||
|
||||
If you want to verify payments in the background, you can set up a webhook in your backend like this: | ||||
|
||||
```python | ||||
@app.route("/webhook", methods=["POST"]) | ||||
def webhook(): | ||||
signature = request.headers.get("x-notch-signature") | ||||
hash_value = hashlib.sha256(HASH_KEY).hexdigest() | ||||
if hash_value == signature: | ||||
try: | ||||
json_data = request.get_json() | ||||
logger.info("Webhook data:", json_data) | ||||
return "", 200 # OK | ||||
except Exception as e: | ||||
logger.info("Error parsing JSON:", e) | ||||
abort(400) # Bad request | ||||
else: | ||||
logger.info("Webhook request could not be verified.") | ||||
abort(403) # Forbidden | ||||
``` | ||||
|
||||
Notchpay will then send the response of each payment to your webhook and depending on the result, you can continue your process, such as validating an order for example. | ||||
|
||||
If you're on localhost, you'll need to install [Ngrok](https://ngrok.com/) to make your URL public and go to the Notchpay administration interface to define your webhook with a key and your URL. | ||||
|
||||
The key will be the one you will use as **HASH_KEY** to authenticate your server so that Notchpay recognizes your signature. | ||||
|
||||
That's all for now. | ||||
|
||||
You can find the complete code [here on GitHub](https://github.com/abdounasser202/just-for-fun/tree/main/notchpay) | ||||
|
||||
If you have any questions, don't hesitate to ask in the OSS Telegram Channel. | ||||
|
||||
Ciao ! | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
We do hope to have you back :) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.