AWS Builder Center

How I Built a Serverless Accounting Pipeline with AWS

A practical look at how I built a fully serverless accounting pipeline with AWS, connecting a WordPress shop, a payment provider, and an accounting tool. The workflow automates invoice generation, storage, email delivery, payment tracking, payout reconciliation, and accounting without running a dedicated backend server.

When I started looking at the accounting side of an online shop, I quickly realized that there are quite a few steps between a customer placing an order and that transaction finally appearing correctly in the accounting system.
An order needs an invoice. The invoice needs to be generated, stored, and emailed. The payment needs to be tracked until the money actually reaches the bank account. And finally, the payment, fees, and payout need to be reconciled in the accounting tool.
Rather than building another traditional backend application to handle all of this, I decided to build the whole process using AWS serverless services.

Starting with the order

The process starts in the WordPress shop. Once an order has been paid, WordPress sends the order information and payment transaction ID to an API endpoint. API Gateway starts a Step Functions workflow, which takes care of the rest.
One of the Lambda functions retrieves additional transaction information from the payment provider. Depending on what is available, this can include details such as the last digits of a card or other payment identifiers.
The invoice itself is generated by an external invoice service. Once the PDF is available, I store it in S3 so there is a permanent archive of the invoice.
Another Lambda function takes care of sending the invoice to the customer.
Architecture of the invoice service

Keeping track of everything

I use DynamoDB as the transaction ledger for the system. For each order, I store information such as the WordPress order ID, payment transaction ID, invoice reference, amount, payment-provider fee, invoice location, and the current processing status.
The status is particularly useful because the payment lifecycle doesn't end when the invoice is created. It also gives me a place to make the workflow idempotent. If WordPress retries the request, I don't want to generate a second invoice for the same order.
For production, I would use DynamoDB conditional writes to make this protection atomic, especially if two requests for the same order arrive at nearly the same time.

Waiting for the payout

This is where the workflow gets a little more interesting. The customer may have paid successfully, but the money hasn't necessarily arrived in the bank account yet. The payment provider can take several days or longer before completing the payout.
Therefore, EventBridge periodically triggers a Lambda function that checks transactions which are still waiting for settlement. The Lambda looks up the current payment status and checks whether the transaction has been included in a completed payout. Once it has, I update the corresponding DynamoDB record to payout-done.
A separate Step Functions workflow periodically looks for these records and starts the accounting process. This separation turned out to be useful because invoice generation and accounting have very different lifecycles. The invoice can be created immediately, while the accounting process can wait for the actual settlement.

Why I chose serverless

For this particular workload, a traditional always-running backend would mostly be waiting. An order might trigger a workflow, then nothing happens for days while waiting for the payout. That makes serverless a natural fit.
Each component has a relatively small responsibility:
  • API Gateway receives requests from WordPress.
  • Step Functions orchestrates the workflows.
  • Lambda handles the custom logic.
  • DynamoDB stores the transaction state.
  • S3 stores the generated invoices.
  • EventBridge triggers the periodic checks.
Another benefit is that the different parts can fail independently. If sending an email fails, I can retry that operation without generating another invoice. If the accounting tool is temporarily unavailable, I can retry the accounting workflow without touching the original order or invoice.
Of course, serverless doesn't remove the need to think about reliability. Idempotency, retries, permissions, monitoring, and error handling are still important, especially when a workflow crosses several independent services.

Final thoughts

What started as a simple requirement "create an invoice for an order and put it into accounting" turned into a small event-driven financial workflow. The most useful design decision was separating the invoice process from the payout and accounting process. The invoice can be generated and delivered immediately, while the accounting workflow waits until the money has actually been settled.
For me, this is one of the nice things about building with AWS serverless services: instead of creating one large application that owns the entire process, I can break the workflow into small, independently retryable steps and let AWS handle the infrastructure between them. And the best part is that there is no accounting server sitting around doing nothing while everyone waits for the next payout.
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article