Skip to content
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

feat(integration): add payment solution integration through stripe ecosystem #511

Merged
merged 5 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions integrations/stripe_payment_solution/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Stripe Payment Solution

Ever had an idea to use Stripe as a payment partner for your business, That's why I've harnessed the power of Fetch.AI
Agent and Stripe payment ecosystem to develop a **Stripe Payment Solution** integration, designed to transform all your
payment requirements, into a simple and convenient manner.

## Table of Contents

- [Introduction](#introduction)
- [Features](#features)
- [How It Works](#how-it-works)
- [Installation](#installation)
- [Usage](#usage)
- [Contributing](#contributing)
- [License](#license)

## Introduction

**Stripe Payment Solution** is an innovative tool that leverages Fetch.AI's Agent technology to help anyone with a requirement to accept payment over the Internet. This System integrates Stripe as the Payment regulator for all the payment related services.

## Features

- Real Time Payment Link Generator.
- Real Time Payment Confirmation mechanism.
- Custom Multi Cart Item Payments.
- Simple UI.
- Automated Invoicing & Receipts
- One-Click Payments for Customers

## Working Diagram

![Working Diagram](./images/payment_system_overview.png "Working Diagram")

This Agent can work like a template and can be used in different organizations with their API keys from stripe A/c.


## Demo Video

https://github.com/user-attachments/assets/3de86092-e2aa-43f1-b59d-73b725698600


## Detail Document

[Detail Document](https://github.com/user-attachments/files/16706323/Stripe_Agent_Integration.pdf)


## How It Works

Stripe Payment Solution few items to choose from, for testing purposes.

1. Choose one of the items to initiate the payment.
2. Then a payment link from Stripe is generated for payment, Click it.
3. Fill it the Payment Details and click **Place Order**, wait for Payment Confirmation.

## Installation

To get started with **Stripe Payment Solution**, follow these steps:

1. **Clone the repository**:
```bash
git clone https://github.com/fetchai/uAgents.git
cd stripe-agent

2. **Open a shell**:
```bash
poetry shell
3. **Install dependencies using Poetry**:
```bash
poetry install --no-root

4. Get API Key and Endpoint Secret from [Stripe.com](https://dashboard.stripe.com/login).

5. **Run the webhook**:
```bash
cd src/stripe_webhook
poetry run python webhook.py
6. Using Ngrok or any Tunneling Software, Create secure https endpoint and set it up as a webhook in Stripe dashboard under the webhook section.
7. **Set up .env file**:
To run the demo, you need to add these API keys and URLs in .env file:
```bash
STRIPE_API_KEY='YOUR_STRIPE_KEY'
STRIPE_ENDPOINT_SECRET='YOUR_STRIPE_ENDPOINT_SECRET'
STRIPE_WEBHOOK_URL='YOUR_STRIPE_WEBHOOK_URL'

8. **Run the agent**:
```bash
cd src/stripe_agent
poetry run python agent.py
9. **Run the demo_server**:
```bash
cd src/stripe_demo_server
poetry run python app.py
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions integrations/stripe_payment_solution/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"categories": [
"Stripe",
"Payment Solution"
],
"deltav": false,
"description": "Helps User for there Payment Requirements",
"title": "Stripe Payment Solution"
}
18 changes: 18 additions & 0 deletions integrations/stripe_payment_solution/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[tool.poetry]
name = "general-use-payment-stripe-solution"
version = "0.1.0"
description = "General Use Payment Solution with Stripe and Fetch.AI Agents"
authors = ["Neeraj Bansal <[email protected]>"]
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.10"
uagents = { version = "^0.15.2", python = ">=3.10,<3.13" }
uagents-ai-engine = { version = "^0.5.0", python = ">=3.10,<3.12" }
stripe = "^10.7.0"
Flask = { extras = ["async"], version = "^3.0.3" }
python-dotenv = "^1.0.1"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
157 changes: 157 additions & 0 deletions integrations/stripe_payment_solution/src/stripe_agent/agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import os
import uuid

import requests
from ai_engine import UAgentResponse, UAgentResponseType
from dotenv import load_dotenv
from uagents import Agent, Protocol, Context, Model
from uagents.setup import fund_agent_if_low

class KeyValue(Model):
currency: str
product_name: str
unit_price: int
units: int

class StripePaymentRequest(Model):
item_in_cart: list[KeyValue]
customer_email: str
order_id: str

class QueryPaymentStatus(Model):
client_reference_id: str



class PaymentStatus(Model):
client_reference_id: str
data: dict

load_dotenv()

agent = Agent(
name="Stripe Agent",
seed="Stripe Agent secret seed phrase",
log_level="DEBUG",
endpoint="http://localhost:8001/submit",
port=8001
)
fund_agent_if_low(str(agent.wallet.address()))

print("Agent Address:", agent.address)


stripe_payment_protocol = Protocol("Stripe Payment Protocol", "1.00")

API_KEY = os.getenv('STRIPE_API_KEY', "")
ENDPOINT_SECRET = os.getenv('STRIPE_ENDPOINT_SECRET', "")
WEBHOOK_URL = os.getenv('STRIPE_WEBHOOK_URL', "")
STRIPE_API_URL = os.getenv('STRIPE_API_URL', "")


@stripe_payment_protocol.on_message(model=StripePaymentRequest, replies={UAgentResponse})
async def create_checkout_session(ctx: Context, sender: str, msg: StripePaymentRequest):
ctx.logger.info(
f"Received Request from {msg.customer_email} for Payment Link.")
try:
print(agent.address)
payload = {
'customer_creation': 'always',
'customer_email': msg.customer_email,
'mode': 'payment',
'payment_intent_data[metadata][order_id]': msg.order_id,
'success_url': f"{WEBHOOK_URL}/order/success?session_id={{CHECKOUT_SESSION_ID}}",
'cancel_url': f"{WEBHOOK_URL}/cancel"
}

for index, item in enumerate(msg.item_in_cart):
payload[f'line_items[{index}][price_data][currency]'] = item.currency
payload[f'line_items[{index}][price_data][product_data][name]'] = item.product_name
payload[f'line_items[{index}][price_data][unit_amount]'] = item.unit_price
payload[f'line_items[{index}][quantity]'] = item.units

headers = {
'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/x-www-form-urlencoded'
}

# encoded_payload = urllib.parse.urlencode(payload, doseq=True)
response = requests.post(f"{STRIPE_API_URL}/checkout/sessions", headers=headers, data=payload)
session = response.json()
request_id = str(uuid.uuid4())
if response.status_code == 200 and 'url' in session:
resp = await ctx.send(
sender,
UAgentResponse(
message=str(session['url']),
type=UAgentResponseType.FINAL,
request_id=request_id
),
)

ctx.storage.set(msg.order_id, {'address': sender})
else:
await ctx.send(
sender,
UAgentResponse(
message="Can't generate payment link, please try again after some time.!",
type=UAgentResponseType.FINAL,
request_id=request_id
),
)
except Exception as exc:
ctx.logger.error(exc)
await ctx.send(
sender, UAgentResponse(message=str(exc), type=UAgentResponseType.ERROR)
)


@stripe_payment_protocol.on_message(model=QueryPaymentStatus, replies={UAgentResponse})
async def payment_status(ctx: Context, sender: str, msg: QueryPaymentStatus):
ctx.logger.info(
f"Received query request from {msg.client_reference_id}.")
try:
payment_details = ctx.storage.get(msg.client_reference_id) or None
if 'payment_data' in payment_details:
await ctx.send(
sender, UAgentResponse(message="Payment Succeeded", type=UAgentResponseType.FINAL)
)
else:
await ctx.send(
sender, UAgentResponse(message="Payment confirmation awaited", type=UAgentResponseType.FINAL)
)
except Exception as exc:
ctx.logger.error(exc)
await ctx.send(
sender, UAgentResponse(message=str(exc), type=UAgentResponseType.ERROR)
)


@stripe_payment_protocol.on_message(model=PaymentStatus, replies={UAgentResponse})
async def payment_status(ctx: Context, sender: str, msg: PaymentStatus):
ctx.logger.info(
f"Received Payment Update of Customer {msg.client_reference_id}.")
try:
payment_details = ctx.storage.get(msg.client_reference_id) or None
if payment_details:
payment_details['payment_data'] = msg.data
ctx.storage.set(msg.client_reference_id, payment_details)

await ctx.send(
payment_details['address'], UAgentResponse(message="Payment Succeeded", type=UAgentResponseType.FINAL)
)
print(f"Notified {payment_details['address']} of payment confirmation")
else:
print('Weird...')

except Exception as exc:
ctx.logger.error(exc)
await ctx.send(
sender, UAgentResponse(message=str(exc), type=UAgentResponseType.ERROR)
)


agent.include(stripe_payment_protocol, publish_manifest=True)

if __name__ == "__main__":
agent.run()
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import asyncio
from typing import List

from flask import Flask, render_template, request, jsonify
from pydantic import Field
from uagents import Model
from uagents.communication import send_sync_message


app = Flask(__name__)

class KeyValue(Model):
currency: str
product_name: str
unit_price: int
units: int

class QueryPaymentStatus(Model):
client_reference_id: str

class StripePaymentRequest(Model):
item_in_cart: list[KeyValue]
customer_email: str
order_id: str


@app.route('/', methods=['GET'])
def index():
return render_template('index.html')


@app.route('/test', methods=['GET'])
def test():
return jsonify({"message": "Flask server is running"}), 200


def run_async(coro):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
return loop.run_until_complete(coro)


@app.route('/query', methods=['POST'])
async def query_agent():
payment_status = QueryPaymentStatus(
client_reference_id='123456'
)
response = await send_sync_message(
'agent1qtehcv05d2yfdxgyhk8wnk9pavl7yxyzg2m05gvqwgggwtxas99nugry6xk',
payment_status
)
print(response)

return jsonify(response)


@app.route('/process', methods=['POST'])
async def process():
data = request.json

# Create a StripePaymentRequest object
payment_request = StripePaymentRequest(
item_in_cart=data['item_in_cart'],
customer_email=data['customer_email'],
order_id=data['order_id']
)
print("sending the payment request to Stripe agent")

response = await send_sync_message(
'agent1qtehcv05d2yfdxgyhk8wnk9pavl7yxyzg2m05gvqwgggwtxas99nugry6xk',
payment_request
)
print(response)

return jsonify(response)


if __name__ == '__main__':
app.run(debug=True, port=9000, host='0.0.0.0')
Loading
Loading