Stripe integration for nRF Cloud:
Creates Stripe invoices from nRF Cloud billing data
Every invoice is a legal document.
Very simplified:
Norwegian law (Altinn) requires every sales document to carry:
MVA suffixForetaksregisteret (the Enterprise Register
line)kr symbol is ambiguous)Invoices are rendered by Stripe, not by us.
We cannot assert on rendered output with unit tests:
Run the real pipeline against the real Stripe sandbox:
-, (, :) to
NUL.2026-06-01 → 2026\x0006\x0001
Reading the text layer directly (e.g. with pdf.js) mangles values.
Amazon Textract OCRs the document — it reads the glyphs as they are drawn — and preserves punctuation, spaces and line breaks.
Also provides document structure (Layout, Forms, Tables, Queries, Signatures) but this is not needed for us.
import {
StartDocumentTextDetectionCommand,
TextractClient,
} from "@aws-sdk/client-textract";
const textract = new TextractClient();
const { JobId } = await textract.send(
new StartDocumentTextDetectionCommand({
DocumentLocation: {
S3Object: { Bucket: documentsBucketName, Name: key },
},
}),
);StartDocumentTextDetection kicks off an
asynchronous job. It returns a JobId
immediately and processes the document in the background.
const { JobStatus, Blocks } = await waitForIt(async () => {
const result = await textract.send(
new GetDocumentTextDetectionCommand({ JobId }),
);
if (result.JobStatus === "IN_PROGRESS") throw new Error("not done yet");
return result;
}, 40); // ~3 minutes of backoff
const lines = (Blocks ?? [])
.filter((b) => b.BlockType === "LINE" && b.Text)
.map((b) => b.Text);
const text = lines.join("\n");Visualized on AWS Console
Visualized on AWS Console
On the rendered PDF (OCR’d text):
OCR tests are slow (Textract) and cost money¹, so they run only when a change can affect invoice rendering:
ocr-e2e:
needs: [stack-prefix, changes, tests]
if: ${{ needs.changes.outputs.ocr == 'true' }}
strategy:
fail-fast: true
max-parallel: 1
matrix:
spec:
- chargedInvoiceDocument.ocr.spec.ts
- vatDeclarationNorwegianCustomer.ocr.spec.ts
- perProjectGrouping.ocr.spec.ts
# … one job per specEach spec runs as its own job in a
fail-fast, max-parallel: 1 matrix, so the
first failure cancels the rest.
¹ 1 USD per 1.000 documents.
Every rendered invoice PDF is written to the git-ignored
e2e-invoices/ folder and uploaded as a CI artifact:
- name: Upload the rendered invoice PDF
uses: actions/upload-artifact@v4
with:
name: e2e-invoices-${{ matrix.spec }}
path: e2e-invoices/
retention-days: 7A failing assertion includes the full OCR’d text in its message, and the PDF is a click away.
Please share your feedback!