doc-site
Architecture

Membership passes

How a membership pass is bought, how its balance is stored, how the cart decides which button to show, and what happens when a pass is used.

A membership pass is a prepaid bundle of camps. Buying one sets a balance on the customer's Shopify account. Using one replaces a camp in the cart with a zero-cost copy of that camp, and the balance goes down after checkout.

Buying a pass

Membership pass products are ordinary products listed on the memberships page from the membership-passes collection.

  1. The customer buys a pass and checks out.
  2. Zapier's "Membership Pass Purchase via API" zap calls POST /memberships on shopify-zoho-integration.
  3. The service creates a Membership Purchase record in Zoho and sets the customer's metafields in the membership_pass namespace.
Customer metafieldMeaning
membership_pass.statusactive while the pass can be used
membership_pass.total_campsCamps bought
membership_pass.remaining_campsCamps left
membership_pass.redeemed_campsCamps used
membership_pass.membership_idsZoho membership purchase codes

The service gives a 3-camp pass 12 months, a 5-camp pass 18 months and a 7-camp pass 24 months, and stores the expiry date on the Zoho Membership Purchase record. A daily Zoho function clears the Is Active flag on expired Zoho records.

The expiry date is not copied to Shopify. The cart and asc-api-membership-pass check only status and remaining_camps, so a pass that has passed its expiry date can still be used unless someone changes status from active.

The cart button

For each camp line, the cart shows one of these states. The logic is in theme/sections/asc-new-cart-layout.liquid.

CustomerPass balanceCampButton textClick
Logged outAnyAny"Login to use membership pass", or the section's Login Text settingGoes to the login page
Logged inStatus not active, or no camps leftAny"Membership pass not applicable for this camp"Goes to the account login URL
Logged inActive with camps leftPartner program, or title contains "community sports""Membership pass not applicable for this camp"Explains that passes only apply to camps run by ASC
Logged inActive with camps leftASC camp"Use membership pass"Uses the pass
AnyAnyThe line is already a pass productHiddenNone

A line is treated as a camp when its product is tagged sport.

The partner and Community Sports Camps exclusion is enforced only by this cart logic. asc-api-membership-pass checks the balance but not the camp type.

documentation/MEMBERSHIP.md and documentation/diagrams/membership-pass-cta.svg describe theme/snippets/asc-cart-actions.liquid. That snippet belongs to the previous cart section, which is disabled, and its logged-out behaviour differs from the table above.

Using a pass

The customer clicks Use membership pass on the cart page. asc-api-membership-pass checks the customer metafields, creates a zero-cost pass product that copies the camp, and swaps the camp line for it. Nothing is deducted until PUT /memberships runs after checkout.

  1. The customer clicks Use membership pass. The browser reads the cart token and sends it, with the customer id, email, variant id and the line's participant properties, to POST {Google Cloud API URL}v1/customers/{id}/membership-pass/use.
  2. asc-api-membership-pass checks that the pass is active and that remaining camps, less any pass products already in the cart, is at least one.
  3. It creates a pass product:
    • title "Membership Pass: " followed by the camp's title;
    • product type and tag membership-pass-product, plus the tag sport;
    • template product.redirect, which sends anyone who opens it to the home page;
    • metafield membership_product.real_product pointing at the original variant;
    • price $0, one unit of stock, no shipping.
  4. It swaps the camp line for the pass product in the customer's cart, keeping the participant properties, and builds a copy of the cart with the same change.
  5. The browser stores the new cart token in the cart cookie and reloads the page.

Nothing is deducted at this point. After checkout, PUT /memberships on shopify-zoho-integration counts the pass products in the order, reduces remaining_camps and increases redeemed_camps. The zap that makes this call is not named in any repository.

The order line is tagged sport, so POST /orders records it as an ordinary booking in Zoho.

Pass products after checkout

Pass products are hidden from the storefront by the redirect template. A Zapier zap named "Auto-archive membership products" archives them. The zap's trigger and its 15-minute delay are configured in Zapier.

Failure modes

SymptomLikely cause
"Membership pass not applicable" for a memberstatus is not exactly active, or remaining_camps is 0 or blank
"That didn't work..." after clickingAPI rejected the request. The message names the reason, for example "Pass already applied for this line item"
Balance not reduced after checkoutPUT /memberships did not run for that order
Pass products visible in Products listAuto-archive zap has not run or is off

Steps are in The membership pass is not applying.

Pending change

Branch 95-asc-073-fx-au-memberships-revised-cart-messaging-and-info-box in asc-shopify-src, and 96-asc-073-… in the theme repository, changes the cart states. A logged-in customer with no camps left will see "Your account does not have any membership passes left", and partner programs will show as not applicable whatever the balance. Update this page when it merges.

Source map

ConcernLocation
Cart button statestheme/sections/asc-new-cart-layout.liquid
Button click handlerssrc/js/custom.js
API root settingTheme settings, ASC - Membership Pass, Google Cloud API URL (gcloud_api_root)
Pass useasc-api-membership-pass repository, src/services/redemption.service.ts, src/services/pass-product.service.ts
Purchase and post-checkout balanceasc-api-src-zoho-integration repository, src/features/membership-pass/
Redirect templatetheme/templates/product.redirect.json

On this page