Review
Guest kiosk checkout for Salt & Stem market bouquets
Salt & Stem walk-up kiosks were creating a Stripe Customer per bouquet (noise in Dashboard, hit Customer create rate limits on Mother's Day). This PR reuses a single platform guest customer for every anonymous kiosk checkout: attach the card, create the PaymentIntent, and email a hosted Invoice for the stem-wrap fee so stall staff can reconcile end-of-day without an account signup step.
StripeTier 5stripepaymentsmulti-tenantguest-checkout
Click a line to flag it, pick one or more labels, then submit. If the change looks correct, approve it.
billing/kiosk_checkout.py+40-18
| 1 | 1 | import stripe | |
| 2 | 2 | from app.config import settings | |
| 3 | 3 | from app.orders import KioskOrder, mark_paid | |
| 4 | 4 | ||
| 5 | 5 | stripe.api_key = settings.STRIPE_SECRET_KEY | |
| 6 | 6 | ||
| 7 | - | def charge_kiosk_guest(order: KioskOrder, payment_method_id: str) -> dict: | |
| 8 | - | customer = stripe.Customer.create( | |
| 9 | - | email=order.receipt_email, | |
| 10 | - | name=order.buyer_name or "Kiosk guest", | |
| 11 | - | metadata={"order_id": order.id, "stall_id": order.stall_id}, | |
| 12 | - | ) | |
| 13 | - | stripe.PaymentMethod.attach(payment_method_id, customer=customer.id) | |
| 14 | - | intent = stripe.PaymentIntent.create( | |
| 15 | - | amount=order.total_cents, | |
| 16 | - | currency="usd", | |
| 17 | - | customer=customer.id, | |
| 18 | - | payment_method=payment_method_id, | |
| 19 | - | confirm=True, | |
| 20 | - | receipt_email=order.receipt_email, | |
| 21 | - | metadata={"order_id": order.id}, | |
| 22 | - | ) | |
| 23 | - | mark_paid(order.id, intent.id, customer.id) | |
| 24 | - | return {"payment_intent": intent.id, "customer": customer.id} | |
| 7 | + | # One reusable Stripe customer for all anonymous kiosk buyers — keeps Dashboard tidy | |
| 8 | + | GUEST_CUSTOMER_ID = settings.STRIPE_GUEST_CUSTOMER_ID # e.g. cus_SaltStemGuest | |
| 9 | + | ||
| 10 | + | def charge_kiosk_guest(order: KioskOrder, payment_method_id: str) -> dict: | |
| 11 | + | stripe.PaymentMethod.attach( | |
| 12 | + | payment_method_id, | |
| 13 | + | customer=GUEST_CUSTOMER_ID, | |
| 14 | + | ) | |
| 15 | + | intent = stripe.PaymentIntent.create( | |
| 16 | + | amount=order.total_cents, | |
| 17 | + | currency="usd", | |
| 18 | + | customer=GUEST_CUSTOMER_ID, | |
| 19 | + | payment_method=payment_method_id, | |
| 20 | + | confirm=True, | |
| 21 | + | receipt_email=order.receipt_email, | |
| 22 | + | metadata={"order_id": order.id, "stall_id": order.stall_id}, | |
| 23 | + | ) | |
| 24 | + | # Hosted invoice for wrap/delivery fee so stalls can print the PDF at close | |
| 25 | + | invoice = stripe.Invoice.create( | |
| 26 | + | customer=GUEST_CUSTOMER_ID, | |
| 27 | + | collection_method="send_invoice", | |
| 28 | + | days_until_due=0, | |
| 29 | + | metadata={"order_id": order.id}, | |
| 30 | + | auto_advance=True, | |
| 31 | + | ) | |
| 32 | + | stripe.InvoiceItem.create( | |
| 33 | + | customer=GUEST_CUSTOMER_ID, | |
| 34 | + | invoice=invoice.id, | |
| 35 | + | amount=order.wrap_fee_cents, | |
| 36 | + | currency="usd", | |
| 37 | + | description=f"Stem wrap · {order.stall_id}", | |
| 38 | + | ) | |
| 39 | + | finalized = stripe.Invoice.finalize_invoice(invoice.id) | |
| 40 | + | stripe.Invoice.send_invoice(finalized.id) | |
| 41 | + | mark_paid(order.id, intent.id, GUEST_CUSTOMER_ID) | |
| 42 | + | return { | |
| 43 | + | "payment_intent": intent.id, | |
| 44 | + | "invoice": finalized.id, | |
| 45 | + | "customer": GUEST_CUSTOMER_ID, | |
| 46 | + | } |