Brewing Coffee : Setting Up Payment with Temporal Activities

In the last post, we created a basic Temporal Workflow to handle coffee orders. However, it didn’t actually do anything yet!
In this post, we’ll start adding functionality by creating the Payment Service — a dedicated micro-service that will calculate billing amounts for coffee orders.
Overview
Our payment-service will:
Receive a coffee order.
Calculate the total bill.
Return the billing amount back to the workflow.
For simplicity -
Each coffee costs $2.00 (we represent it as 200 cents internally).
We’ll communicate all amounts in integer cents to avoid floating point precision issues.
Step 1 : Setting-up Payment Service
Let’s create the basic file structure for payment-service:
best-coffee-shop> mkdir payment-service
best-coffee-shop> cd payment-service
best-coffee-shop/payment-service> touch activity.py main.py
Step 2: Writing the Billing Activity
our payment service will have
acitivity.py : contains our activity logic
main.py : register activity worker
file payment-service/activity.py
from temporalio import activity
from dataclasses import dataclass
@dataclass
class CoffeeOrder:
customer_name: str
order_number: str
quantity: int = 1
@dataclass
class OrderBill:
order_number: str
amount: int
COFFEE_UNIT_PRICE = 200
@activity.defn(name='BillCalculationActivity')
async def calculate_payment(order: CoffeeOrder) -> OrderBill:
bill_amount = order.quantity * COFFEE_UNIT_PRICE
return OrderBill(
order_number=order.order_number,
amount=bill_amount
)
This defines an Activity named "BillCalculationActivity". Our workflow will reference this name to invoke the right activity.
It receives a CoffeeOrder and returns an OrderBill with the total amount.
Note : In real applications, it’s better to share constants like activity names across projects using a common package. For python check out this StackOverflow discussion and explore tools like Poetry for dependency management.
file payment-service/main.py
import asyncio
from temporalio.worker import Worker
from temporalio.client import Client
from activity import calculate_payment
from temporal_config import TEMPORAL_ADDRESS, TASK_QUEUE
async def main():
client = await Client.connect(TEMPORAL_ADDRESS)
worker = Worker(
client,
task_queue=TASK_QUEUE,
activities=[calculate_payment],
)
await worker.run()
if __name__ == "__main__":
asyncio.run(main())
This worker connects to Temporal and listens for BillCalculationActivity execution requests.
Step 3 : Move Configuration to separate file
We will move our temporal configuration to separate file.
file : payment-service/temporal_config.py
TEMPORAL_ADDRESS = 'temporal:7233'
TASK_QUEUE = 'payment-service'
file : orchestrat-service/temporal_config.py
TEMPORAL_ADDRESS = "temporal:7233"
TASK_QUEUE = "best-coffee-orders"
Update imports in orchestrator-service/main.py
Step 4 : Update Workflow
Update the Workflow to invoke the payment calculation:
File: orchestrator-service/workflow.py
from temporalio import workflow
from dataclasses import dataclass
from datetime import timedelta
from temporal_config import TASK_QUEUE
@dataclass
class CoffeeOrder:
customer_name: str
order_number: str
quantity: int = 1
@workflow.defn(name="CoffeeOrderWorkflow")
class CoffeeOrderWorkflow:
@workflow.run
async def run(self, order: CoffeeOrder):
workflow.logger.info(f"CoffeeOrderWorkflow: New order received {order}")
bill = await workflow.execute_activity(
"BillCalculationActivity",
order,
task_queue=TASK_QUEUE,
start_to_close_timeout=timedelta(seconds=15),
)
return bill
Now Workflow will start invoking payment-activity.
Step 5 : Update docker-compose.yml
Append docker-compose.yml
with following
payment-service:
container_name: payment-service
image: python:3
depends_on:
- temporal
volumes:
- ./payment-service:/app
- ./requirements.txt:/requirements.txt
- ./init.sh:/init.sh
entrypoint: sh -c "chmod -R 755 /app && chmod -R 755 /init.sh && sh /init.sh"
networks:
- best-coffee-network
Step 6 : Try-it Out
Clean up old containers (optional):
best-coffee-shop> docker-compose down
best-coffee-shop> docker system prune -a -f
Run
best-coffee-shop> docker-compose up
Visit http://localhost:8080 (Temporal UI) Click Start Workflow → by providing following
Workflow ID = best-coffee-1
Task Queue = best-coffee-orders
Workflow Type = CoffeeOrderWorkflow
Data = {"customer_name": "Aniruddha", "order_number": "best-coffee-2", "quantity": 1}
Encoding = json/plain
You should see the workflow executing the BillCalculationActivity.
Click on the activity to inspect:
Input: Coffee order details
Output: Calculated bill (e.g., $2.00)
You can also switch to the JSON tab to see how Temporal handles the workflow execution behind the scenes.
How Temporal Handles Workflow Execution
Every statement inside a workflow becomes a task for Temporal:
workflow.logger.info(...) → generates a WorkflowTask
- Events: WorkflowTaskScheduled → WorkflowTaskStarted → WorkflowTaskCompleted
await workflow.execute_activity(...) → generates an ActivityTask
- Events: ActivityTaskScheduled → ActivityTaskStarted → ActivityTaskCompleted
Also notice:
workerVersion fields differ between Workflow Tasks and Activity Tasks.
Different workers execute workflows and activities!
Code for this post
You can find the code up to this 👉: Code repo
Subscribe to my newsletter
Read articles from Aniruddha directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by