Home / Help / Reference

Ship’s Store deployment

Store-related deployment, secrets, and operational notes.

This document covers order statuses, email flows, and production deployment for the NSVA Ship's Store.


Order Statuses

Status Counts toward notifications badge? Emails sent
PENDING No
PAID Yes Order confirmation (on payment)
PROCESSING Yes
CONFIRMED No Order confirmation
SHIPPED No Order shipped (with tracking)
DELIVERED No
CANCELLED No
REFUNDED No

PENDING (unpaid) and CONFIRMED do not count toward the notifications badge. Only PAID and PROCESSING orders need fulfillment action.


Email Flows

1. Order placed (before payment)

  • No emails sent — avoids emailing when payment is abandoned/fails
  • Trigger: POST /api/store/orders

2. Payment confirmed

  • Customer: "Order confirmed"
  • Admin: "New order" (paid)
  • Trigger: Stripe webhook checkout.session.completedconfirmOrderPayment()

3. Admin sets status to CONFIRMED

  • Customer: "Order confirmed"
  • Trigger: PATCH /api/admin/orders/[id] with status: "CONFIRMED"

4. Admin sets status to SHIPPED

  • Customer: "Order shipped" (includes tracking number if set)
  • Trigger: PATCH /api/admin/orders/[id] with status: "SHIPPED"

Environment Variables

Email (Microsoft 365 — production)

Variable Required Description
EMAIL_PROVIDER Yes smtp for production (M365)
SMTP_HOST Yes smtp.office365.com
SMTP_PORT Yes 587 (with SMTP_SECURE=false)
SMTP_SECURE Yes false for STARTTLS on 587
SMTP_USER Yes Sending mailbox, e.g. noreply@nsva.org
SMTP_PASS Yes Mailbox or app password (tenant policy)
EMAIL_FROM Yes e.g. NSVA <noreply@nsva.org>
EMAIL_OVERRIDE_TO No Never set in production (redirects outbound mail)

Payments (Stripe)

Variable Required Description
PAYMENT_PROVIDER Yes stripe
STRIPE_SECRET_KEY Yes sk_live_... for production
STRIPE_WEBHOOK_SECRET Yes whsec_... from Stripe webhook

Production Deployment Checklist

1. Microsoft 365 (email)

  • Create or designate noreply@nsva.org (or approved sender) in the NSVA tenant
  • Enable SMTP AUTH on that mailbox if required by policy
  • Store SMTP_USER / SMTP_PASS in Secret Manager (nsva-smtp-user, nsva-smtp-pass)
  • Store EMAIL_FROM in nsva-email-from (e.g. NSVA <noreply@nsva.org>)
  • Do not set EMAIL_OVERRIDE_TO in production

If emails still don’t send in production:

  1. Cloud Run logs — Look for [EmailService] SMTP failed: and the error message (authentication, relay, etc.).
  2. Credentials — Confirm SMTP_USER / SMTP_PASS and that the mailbox allows SMTP submission.
  3. Test endpointGET https://your-run-url.run.app/api/test-email?to=recipient@example.com (only when ALLOW_TEST_EMAIL=true in production). Failed sends return deliveryError in the JSON body.

2. Stripe (Payments)

  • Switch to live account in Stripe Dashboard
  • Complete business verification
  • Create secret nsva-stripe-secret-key with live secret key (sk_live_...)
  • Create webhook in Stripe Dashboard:
    • URL: https://nsva-frontend-nxgr3bdqqq-uc.a.run.app/api/webhooks/stripe
    • Event: checkout.session.completed
  • Create secret nsva-stripe-webhook-secret with webhook signing secret (whsec_...)

3. GCP Secret Manager

Required secrets for production:

Secret name Contents
nsva-database-url PostgreSQL connection string
nsva-jwt-secret JWT signing secret
nsva-jwt-refresh-secret Refresh token secret
nsva-encryption-key 32-char encryption key
nsva-smtp-user M365 mailbox user (e.g. noreply@nsva.org)
nsva-smtp-pass M365 password or app password
nsva-email-from Sender display, e.g. NSVA <noreply@nsva.org>
nsva-stripe-secret-key Stripe live secret key
nsva-stripe-webhook-secret Stripe webhook signing secret
nsva-pii-purge-secret For PII purge cron (optional)
nsva-cron-secret NSVA_CRON_SECRET — auth for /api/cron/membership-renewal-reminders and /api/cron/commander-digest

4. Cloud Build

cloudbuild.yaml sets EMAIL_PROVIDER=smtp, Microsoft 365 SMTP host/port, and wires SMTP + EMAIL_FROM secrets to Cloud Run. Push to main to trigger deployment.

5. NSVA cron jobs (membership renewal & Commander digest)

These are not part of the Ship’s Store; they apply to the whole site. Configure Google Cloud Scheduler to POST the cron routes with NSVA_CRON_SECRET.

6. Object storage (GCS) — uploads, images, DD-214, archives

When STORAGE_PROVIDER=gcs (set in cloudbuild.yaml), the app needs a bucket and IAM on the Cloud Run service account.

  1. Create a bucket (name must match GCS_BUCKET; default in cloudbuild.yaml is nsva-489001-files via _GCS_BUCKET). GCS names are global—if taken, pick another name and set _GCS_BUCKET on the trigger.

    gsutil mb -p nsva-489001 -l US-CENTRAL1 gs://nsva-489001-files
    
  2. Grant the Cloud Run runtime identity access to objects in that bucket (typically the default compute service account PROJECT_NUMBER-compute@developer.gserviceaccount.com, or the custom SA if you attach one):

    • Role: Storage Object Admin (roles/storage.objectAdmin) on that bucket, or a tighter custom role if you prefer.
  3. Deploy a build that includes @google-cloud/storage and GCS_BUCKET in the service env (already wired in cloudbuild.yaml).

  4. Manual CLI deploys: pass the same vars, e.g. gcloud run deploy ... --set-env-vars=STORAGE_PROVIDER=gcs,GCS_BUCKET=nsva-489001-files,....

Public pages load allowed prefixes through GET /api/files/object/.... Private object keys (for example under dd214/ and archive/) are not exposed through public URLs.

Order lifecycle, Stripe checkout, webhooks, notifications, and admin order tools run in the deployed Cloud Run application using the environment variables and secrets above.