This project integrates your local customer catalog with external services like Stripe and potentially Salesforce. It uses FastAPI for the web server, Celery for asynchronous task processing, Docker for containerization, and Ngrok for local webhook testing.
- Getting Started
- Running with Docker
- Running Locally
- Setting Up Ngrok for Webhooks
- Environment Variables
- API Endpoints
- Answers to throretical questions
- Answers to throretical question 2
- Python 3.9+
- Docker & Docker Compose (for Docker setup)
- A Stripe account for testing Stripe integration
Be sure to have all the environment variable set up, All the keys and auth token for the webhook
- fastapi
- sqlite
- docker
- stripe API
- Ngrok (webhook)
- Celery (worker)
- rabbitMQ (queue)
git clone https://github.com/mogiiee/Zenskar-Task.git
cd Zenskar-Task
Workflow Diagram
This method uses Docker to run the FastAPI server, Celery worker, and RabbitMQ. It has a docker-compose up file which can start all the docker container at the same time for the ease of deployment.
docker-compose up --build
This command builds the Docker images and starts the containers defined in docker-compose.yml
.
Please wait for about 10 seconds for the server to start up. You should see something like this
its okay for the celery worker to try 3 or 4 times to connect to the server.
both of these scenarios are okay just wait for about 10 seconds.
-
FastAPI server will be accessible at http://localhost:8000/docs or http://0.0.0.0:8000/docs
-
RabbitMQ management interface at http://localhost:15672 (default credentials: guest/guest)
It is not recommended as you might face alot of errors. I have designed it to work with docker-compose ... you will have to change alot of paths manually. Just use docker bro
python -m venv virtualenv
source virtualenv/bin/activate
(linux)
pip install -r requirements.txt
uvicorn app.main:app --reload
In a new terminal session, activate the virtual environment and run: (this is handled in the docker-compose file)
celery -A app.worker.celery_worker worker --loglevel=info
Ngrok allows you to expose your local server to the internet, facilitating webhook testing.
-
Download and install Ngrok from Ngrok's website.
-
Start Ngrok to expose port 8000:
./ngrok http 8000 or ngrok http 8000
-
Copy the forwarding URL provided by Ngrok (e.g.,
https://<random_string>.ngrok.io
). -
Add this URL in the webhook section in the Dashboard of stripe.
Remember to add URL/webhooks/stripe at the end else you would get a 400 error as it wont hit the right endpoint. Been there done that lol.
Create a .env
file or just populate the .env.sample
file in the root directory and add the following variables:
- STRIPE_SECRET_KEY='sk_test.... replace it here'
- STRIPE_PUBLISHABLE_KEY='pk_test_.... replace it here although this is not used in the backend'secret.
there is another .env.sample file in app/webhook_handler/.env.sample
you can poplulate it with
- SIGNING_SECRET="get it from the Stripe dashboard> webhook> signing secret"
- API Key: Found in your Stripe Dashboard under Developers > API keys.
- Endpoint Secret: Create a webhook endpoint in Stripe Dashboard (Developers > Webhooks) using your Ngrok URL. Use the secret provided there.
-
I had to spend alot of time figuring out what the logic will be for the local id and the id mentioned in Stripe
-
If you see my initial commits, I tried using Kafka with a producer and consumer queue. I could not figure out how I can include it as a container and run it with docker-compose. Hence I decided to go with RabbitMQ
- Greeting to the people of Zenskar with love:
GET /
- Create Customer:
POST /customers/
- Update Customer:
PUT /customers/{customer_id}
- Delete Customer:
DELETE /customers/{customer_id}
- Stripe Webhook:
POST /webhooks/stripe
How would the current setup be utilized with salesforce API. With the research I have done, this setup would work fine provided there are 3 key changes done on top of this setup.
-
Salesforce does not have a customers API like stripe but it has Account> Contact where objects like customer details can be stored. Account is where business details are stored and contact is where custormers of that business is stored. Salesforce just like Stripe has a REST API to access these and perform CRUD ops on their dashboard, but it requires secondary authentication with OAuth2, which brings me to my second point.
-
Authentication, now this would have to be implemented completely from scratch as an OAuth flow would be needed to make valid API requests.
-
As we used Stripe's webhook, salesforce also provides a service called Salesforce outbound messages. A new end point could be made or the same one could be updated so that it calls different functions, when different services hit that endpoint. I have put the sample code above the webhook endpoint in comments here.
-
We could also implement a celery beat which hits the Salesforce API as a cron job, and syncs the local db. The functions to sync the local db wll remain the same, only another beat would have to be implemented.
-
Minor changes in the the docker-compose file to make sure that this beat is running when the entire service is up.
Since invoicing is a completely different topic from the customers, there would need to be extentions to some of the code base. The part where both of these can be linked would be where each customer_id has some invoices(status does not matter)
- Celery workers-
-
Similarity: Just as how i have tasks for syncing customer data (update_customer_in_stripe), I can create tasks like create_invoice_in_stripe and update_invoice_status_in_local_db.
-
Improvement: Implement task routing in Celery to separate customer-related tasks from invoice-related tasks, improving task management and scalability.
- Data Model Expansion
- Similarity: Just as i have a customer model that includes Stripe-specific information (stripe_customer_id), i will need an invoice model that might include fields like stripe_invoice_id, amount, status, and a relationship to the customer model.
- Webhook Handling
- Similarity: The existing webhook endpoint (@app.post("/webhooks/stripe")) uses payload validation and event type checking. Similar logic will apply to invoice events, identifying them through event['type'] (e.g., invoice.paid, invoice.payment_failed). This can be checked when creating the webhook on the dashboard.
- API and Authentication
- Since I am using the same stripe API's there would be no issues to get the Stripe's Invoices API. New functions would have to be created to make changes to the invoice, update its status and preprocess it to get important information.
With a few changes to the code, we can implement new functions which write the same to the local db and show all the invoice information which the stripe API is giving in a new table.
- main dashboard of all the endpoints using swagger UI. Can also use Postman if that's more up your alley.
- Be sure to check these events while creating the webhook
- My first customer Christopher Nolan when made directly on the dashboard, will be reflected in app/data/database.db
- all the customers can be created, updated and deleted from the dashboard and everything will reflect in the local database
- creation endpoint
- update endpoint