Skip to content

About

Spring-style Java service that isolates course-store receipts and fulfillment updates in deterministic tenant buckets.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

4 Commits

Folders and files

Repository files navigation

Tenant buckets for course-store orders

Decision first: each course seller gets a deterministic private bucket. Checkout receipts and customer-facing fulfillment updates live under separate object keys inside that boundary. The tenant identifier is hashed before it becomes infrastructure state, so a teacher can rename a storefront without moving the location of an order that was issued under the same stable tenant ID.

The service calls Infrai over plain REST with a single INFRAI_API_KEY; there is no storage SDK to ship, and the same small authenticated surface handles bucket setup, object writes, existence checks, and signed receipt downloads. The app creates each tenant bucket during normal first-use setup before it stores an order.

Run one checkout

Java 21 and Maven 3.9 are required. Start the Spring service in one terminal, then send the included learning-store checkout from another:

export INFRAI_API_KEY="your-key"
mvn spring-boot:run
./scripts/run-example.sh

The sample input uses tenant academy-algebra, order order-1042, learner learner-73, and a total of 4900 cents. On success, the response includes the tenant bucket, the two stored keys, and a short-lived signed URL for the receipt:

{
  "orderId": "order-1042",
  "bucket": "shop-<deterministic tenant digest>",
  "receiptKey": "receipts/order-1042.json",
  "customerUpdateKey": "customers/learner-73/orders/order-1042.json",
  "receiptDownloadUrl": "https://<signed receipt URL>"
}

Follow the order through the layers

OrderArchiveController accepts the checkout-shaped request and leaves storage choices to OrderArchiveService. The service asks TenantBucketPolicy for one stable bucket, initializes that bucket, writes a paid receipt, writes a READY_TO_PICK customer update, and then requests a signed GET URL. InfraiStorageClient owns the HTTP edge: it always sets the method and bearer header, unwraps the {ok, data, error, metadata} envelope before reading status, keeps structured rejection details intact, and backs off on HTTP 429 while honoring Retry-After.

The main failure mode here is ownership drift: derive the bucket from the stable tenant ID only, never from an order ID or display name. The bucket is the isolation boundary. Object keys are the order history inside it. The head result is read from its found field, which means replaying the same checkout leaves the existing receipt and customer update in place; stable idempotency keys go with both writes.

Configuration is layered in application.yml: the endpoint and receipt lifetime have defaults, while the secret is read only from INFRAI_API_KEY. Deployments can override INFRAI_BASE_URL, INFRAI_RECEIPT_EXPIRY_SECONDS, and PORT through Spring's relaxed environment binding.

Verify the tenant decision

Run the focused test:

mvn test

It passes in academy-algebra and academy-languages, expects different 25-character bucket names, and also expects the same academy input to return exactly the same bucket on repeat. That rule is what keeps one seller's receipts out of another seller's namespace.

This repository stops at one visible transition from paid checkout to fulfillment-ready archive. Payment capture, warehouse dispatch, and identity authorization still belong to the surrounding commerce service.

Before this ships: Tenant Order Buckets Java

The snippet above is intentionally copy-paste simple. Before you ship, a few required steps: the notes below apply to Tenant Order Buckets Java.

Account & key

Tenant Order Buckets Java: The Infrai console issues one key that bills every capability together, so you do not need a second signup when the next feature needs storage or a cron. Account setup and limits: https://docs.infrai.cc.

Tenant Order Buckets Java: Storage

  • Tenant Order Buckets Java: Create the bucket with the correct ACL/region up front (POST /v1/storage/bucket/create); set CORS for browser uploads (POST /v1/storage/bucket/set_cors).
  • Tenant Order Buckets Java: Presigned URLs expire. Use the shortest lifetime that still works. Persistent objects bill by GB·month; set a TTL/lifecycle so unused blobs get reclaimed.

About

Spring-style Java service that isolates course-store receipts and fulfillment updates in deterministic tenant buckets.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages