Presale key concepts

How things are organised in Submarine, what they’re called and how they interact with Shopify.

1. Nomenclature

There are a number of resources that play together in Submarine. Some of them (such as customers, products and product variants) require no introduction, since they have identical or similar representations inside Shopify. Others, however, are perhaps less familiar, and it is these that we describe below.

💡
Shopify purchase options Submarine’s integration with Shopify leverages native behaviour around Purchase Options and Selling Plans. We assume that the reader is well versed in those concepts.

1.1. Channel

A channel represents an individual Shopify store. Although you should rarely need to interact directly with channels, most of the resources described here are scoped to a channel, so it makes sense to introduce the concept.
Attributes
  • external ID The external ID of the channel. For Shopify, this is the ID of the store, e.g. 276250199.
  • identifier A unique, human readable identifier for the channel. For a Shopify store, this is the Shopify domain, e.g. ”my-submarine-store.myshopify.com”.
  • status Can be one of “active” or “inactive”. For Shopify, an example of an inactive channel is when the Submarine app is uninstalled from a store.

1.2. Presale campaign

At the heart of Submarine’s presale offering are presale campaigns. A presale campaign represents a sale event for one or more products on a merchant’s store, where payment for that sale isn’t to be taken immediately, but rather at a later date when particular preconditions are met.
For Shopify, each presale campaign is intrinsically linked with a purchase option (and a selling plan). Updates to the presale campaign in Submarine are immediately reflected in the underlying purchase option on Shopify.
Attributes
  • campaign items A list of items (products or variants) linked to the campaign.
  • description The name to be used for the Shopify selling plan. This is exposed to customers on the product page.
  • end at The date the campaign will end. After this date, customers will not be able to purchase the presale.
  • fulfil at The (optional) date the campaign will fulfil. If set, the fulfilment criteria will be extended so that the date needs to have passed as long as inventory having been received.
  • grace period The grace period provided to customers, so that they can rectify their payment details should any payment fail. If this is not set, then customers have just one chance to pay for their presale before Submarine cancels their campaign order.
  • deposit amount
    • The deposit amount allows you to define a percent amount between 0% and 100% to capture during checkout. The default deposit is 0. If a 100% deposit is taken the customer will be able to use any store payment method available. Note: Deposits are not supported for Crowdfunding campaigns.
  • launch at The date the campaign will launch. Before this date, customers will not be able to purchase the presale.
  • limit The maximum combined units of the items that can be sold as part of the campaign. It should be stressed that this is the total across all campaign items, and is not a per-item limit.
  • name The name to be used for the Shopify selling plan. This is exposed to customers on the product page and cart.
  • reference This is only ever used internally, and aids identification for the merchant while navigating the Submarine UI.

1.3. Crowdfunding campaign

Crowdfunding campaigns are similar to presale campaigns, in that they also represent a type of sale event on a merchant’s store. The key difference is that crowdfunding campaigns have a target number of units to be sold — if the number is reached by the campaign’s end time, the campaign is successful and customers are charged, whereas if the number isn’t reached the campaign fails and all campaign orders are cancelled.

1.4. Campaign item

Campaigns can be defined on one or more products or one or more product variants. Aside from some subtle differences down the line, campaigns defined on products behave in much the same way as those defined on product variants. Campaign items are abstractions of the products or variants that define the campaign.
Attributes
  • product ID The ID of the underlying Shopify product.
  • variant ID The ID of the underlying Shopify variant.

1.4. Campaign order

A campaign order represents the purchase of a campaign item (either presale or crowdfund) by a customer. A campaign order is linked to a single campaign item (i.e. it doesn’t contain multiple line items), although it may have a quantity of more than one.
Attributes
  • campaign item The campaign item associated to this campaign order. At the campaign order level, this will always define a product variant on Shopify.
  • identifier A unique identifier for the campaign order, e.g. “#1000-A”. For Shopify, this identifier references the Shopify order name.
  • payment intent The payment intent associated to this campaign order. For the time being, due to constraints on Shopify’s purchase options, the payment intent will always be defined on the parent campaign order group.
  • payment method The payment method associated to this campaign order. For the time being, due to constraints on Shopify’s purchase options, the payment method will always be defined on the parent campaign order group.
  • quantity The quantity of the campaign item that was purchased. Note that this can change if the customer reduces their commitment.

1.5. Campaign order group

A campaign order group is a collection of campaign orders that share the same payment intent. In the Shopify world, this can easily happen when a cart includes multiple presale/crowdfund items.
It should be noted that every campaign order belongs to a campaign order group, even if the group only comprises that single order.
Attributes
  • identifier A unique identifier for the campaign order group, e.g. “#1000”. For Shopify, this identifier references the Shopify order name.
  • payment intent The payment intent associated to this order group. For the time being, due to constraints on Shopify’s purchase options, the payment intent will always be defined here.
  • payment method The payment method associated to this order group. For the time being, due to constraints on Shopify’s purchase options, the payment method will always be defined here.

1.6 Payment method

A payment method is a means of payment associated to a customer. It can have one or more payment instruments (e.g. card, PayPal), although only one of them can be active.
Because of the constraints of Shopify’s purchase options, payment methods are associated to campaign order groups in Shopify (and not individual campaign orders). This means that the same instrument will be used to pay for all campaign items in a mixed cart.
Attributes
  • customer A reference to the customer that owns this payment method.
  • external ID The ID that the external payment processor assigned to their corresponding payment method.
  • metadata An optional set of arbitrary key/value pairs.
  • status Can be one of “active” or “inactive”. Note that payments cannot be processed against an inactive payment method.
Payment instrument attributes
  • active Whether or not the instrument is active. Although a payment method can have many instruments, only one of them can be active at any time.
  • external ID The ID that the external payment processor assigned to their corresponding payment instrument.
  • external reference A supporting reference for the external payment processor. For Shopify, this is usually set to the ID of the Shopify order, e.g. “gid://shopify/Order/5095136559344”.
  • metadata An optional set of arbitrary key/value pairs.
  • payment processor The external processor used to process the payment. For Shopify Payments, this is “shopify”.
  • type The type of payment instrument. For Shopify, this can currently be “card” or “paypal”.
Card attributes
  • brand The card brand, e.g. “mastercard”, “visa”.
  • expiry month The month the card expires, as an integer, e.g. January is denoted as a 1.
  • expiry year The year the card expires, as an integer, e.g. 2025.
  • external ID The ID that the external payment processor assigned to their corresponding card. For Shopify, this is usually set to the ID of the payment mandate created at checkout, e.g. “gid://shopify/PaymentMandate/2470c5852eca3f416c83b54c84aaa413”.
  • last 4 The last four digits of the card number, e.g. “4242”.
  • metadata An optional set of arbitrary key/value pairs.

1.7. Payment intent

A payment intent represents the intention to settle a deferred payment. This concept is essential for presales and crowdfund campaigns, since some or all of the cost of the campaign is captured some time after checkout.
If campaign items are removed from a campaign order group before payment has been collected, the amount due in the payment intent is reduced. For each such removal, a payment intent adjustment is created, detailing the reduction.
Attributes
  • amount The amount that the payment intent is for. Note that this is the amount after any adjustments have been applied.
  • charges All the charges (successful and unsuccessful) that have been processed for this payment intent.
  • metadata An optional set of arbitrary key/value pairs.
  • payment method A reference to the payment method that will be used to process charges for this payment intent.
  • refunds All the refunds (successful and unsuccessful) that have been processed for this payment intent.
  • status The status of the payment intent.
Payment intent adjustment attributes
  • amount The amount that the adjustment is for.
  • description An optional description for the adjustment.
  • metadata An optional set of arbitrary key/value pairs.
  • payment intent A reference to the payment intent that is being adjusted.

1.8. Charge and refund

Hopefully these two concepts require little elaboration. Both are associated with a payment intent, and detail either a capture or a return of money. Details of unsuccessful charges and refunds are preserved and exposed by Submarine, allowing the merchant and customer to remain in the loop.
Charge attributes
  • amount The amount charged.
  • description An optional description for the charge.
  • external ID The ID that the external payment processor assigned to their corresponding charge.
  • failure code A machine-readable message describing the reason for a charge failure.
  • failure message A human-readable message describing the reason for a charge failure.
  • metadata An optional set of arbitrary key/value pairs.
  • payment instrument A reference to the payment instrument associated to this charge.
  • payment intent A reference to the payment intent associated to this charge.
  • status One of “pending”, “succeeded”, “failed”.
  • type One of “verify”, “authorise”, “capture”, “sale”.
Refund attributes
  • amount The amount refunded.
  • description An optional description for the refund.
  • external ID The ID that the external payment processor assigned to their corresponding refund.
  • metadata An optional set of arbitrary key/value pairs.
  • payment instrument A reference to the payment instrument associated to this refund.
  • payment intent A reference to the payment intent associated to this refund.
  • status One of “pending”, “succeeded”, “failed”.

1.9. Inventory application

The arrival of inventory plays an important part in the lifecycle of a campaign. It’s up to the merchant whether a campaign’s stock in released in one go or piecemeal, but either way an inventory application records every such release. Campaign inventory items are the glue that join campaign items and inventory applications.
Attributes
  • campaign item The campaign item associated to this inventory application. At this level, this will always define a product variant on Shopify.
  • quantity allocated The amount of inventory in this application that has been allocated to campaign orders.
  • quantity received The amount of inventory received in this application.
  • quantity remaining The amount of inventory received that has yet to be allocated.

1.10. Inventory allocation

When inventory is applied to a campaign item, it is distributed equitably between waiting campaign orders on a first-come first-served basis. Inventory allocations record how an inventory application is allocated to the waiting campaign orders, detailing both how much stock was initially received, as well as any items that were returned.
Attributes
  • campaign item The campaign item associated to this inventory allocation.
  • inventory application The inventory application associated to this allocation.
  • quantity The inventory count that has been allocated. This should always be equal to the associated campaign item’s quantity.
  • returned quantity The amount of the allocated inventory that has been returned (as a result of changes to the campaign order).

2. Lifecycles

We detail the lifecycles here of key Submarine resources.

2.1. Presale campaign

Submarine automatically transitions a presale campaign through its lifecycle and performs certain actions as it does so. Merchants can optionally override some of these lifecycle events, and manually trigger transitions via the Submarine UI.
A presale campaign’s lifecycle is exposed via its status, with can be one of the following.
  • pending The campaign has been created, but is waiting to be launched. Customers cannot yet add it to their cart nor can they purchase it at checkout.
    • Note that pending campaigns can be cancelled by the merchant.
  • launched The campaign has been launched (either automatically by Submarine or manually by the merchant). Customers can purchase the campaign at checkout, which will result in a campaign order.
    • Note that launched campaigns can be cancelled by the merchant (associated campaign orders will also be cancelled and customers will be refunded for any deposit).
  • ended The campaign has been ended (either automatically by Submarine or manually by the merchant). Customers can no longer purchase the campaign at checkout and merchants can now start to apply inventory to the campaign.
    • Note that ended campaigns can still be cancelled by the merchant (associated campaign orders will also be cancelled and customers will be refunded for any deposit), although this is the last status that supports cancellation.
  • fulfilling Once the fulfilment criteria have been met, the campaign moves to the fulfilling state. The fulfilment criteria are defined as:
    • for a presale campaign with a defined fulfilment time, both that time being reached and there being at least one unit of inventory added to the campaign; or
    • for a presale campaign without a defined fulfilment time, at least one unit of inventory has been added to the campaign.
    • All available inventory is allocated to campaign orders on a first-come first-served basis, and payments for allocated campaign orders are scheduled for capture.
      If payment succeeds, the corresponding fulfilment hold in Shopify is released, allowing delivery to commence. If payment fails, the grace period for customers to rectify their payment details begins. If subsequent payment attempts fail and the grace period expires, the campaign order is cancelled.
  • completed When all campaign orders linked to a fulfilling presale campaign have either been fulfilled or cancelled, it is transitioned into the completed state.
  • cancelled The presale campaign has been cancelled by the merchant. Note that this can only be triggered when it is in a pending, launched or ended state.
At each stage of a presale campaign’s lifecycle, the associated resources are updated on Shopify, whether that be the associated purchase option or the Submarine metafields on the underlying products and/or variants.

💡
Shopify purchase options limitations Shopify currently doesn’t allow partial payments on an order’s payment mandate — only permitting one deferred capture, for the full balance. This means that if a customer purchases multiple campaign items in the same order, with different end dates, the fulfilling state will be delayed until all campaign orders are ready.
The lifecycle of a presale campaign.
The lifecycle of a presale campaign.

2.2. Campaign order

A campaign order’s lifecycle is exposed via its status, with can be one of the following.
  • pending The campaign order has been created, usually as a result of a customer going through checkout with a campaign item. Depending on the length of the campaign, an order could remain in this status for some time.
    • Note that pending orders can be cancelled by the customer and the merchant (customers will be refunded for any deposit paid).
  • allocated The merchant has applied inventory to the associated campaign, which has been allocated to the campaign order (in full). This is the trigger for payment capture.
    • Note that allocated orders can be cancelled by the customer and the merchant (customers will be refunded for any deposit paid), although this is the last status that supports cancellation.
  • paid Payment for the campaign order has been successfully captured (and the fulfilment process can now be started).
    • If initial payment fails, the order will not transition and will stay in the allocated state. This will remain true until either successive attempts result in a successful capture, or the grace period expires.
  • completed The order has been paid and fulfilled.
  • cancelled The order has been cancelled:
    • manually by the customer;
    • manually by the merchant; or
    • automatically by Submarine (due to the payment grace period expiring).

The lifecycle of a campaign order for a presale.
The lifecycle of a campaign order for a presale.
In addition, separate statuses track in more detail the progress of the payment and fulfilment.
The payment status can be one of the following.
  • pending No attempts to capture payment for the campaign order have been made.
  • submitted Depending on the payment processor, the result of a payment capture is often not immediately known, and is instead delivered asynchronously. The submitted status indicates that Submarine has requested the capture of a payment, but is awaiting confirmation of the result from the processor.
  • paid Payment for the campaign order has been successfully captured.
  • failed An attempt to capture payment for the campaign order has been made, but was unsuccessful. Payment retries can be triggered by the customer and/or merchant. Submarine will also make a final attempt to capture payment at the end of the grace period.
  • refunded Payment for the campaign order has been completely refunded to the customer.
  • partially refunded Payment for the campaign order has been partially refunded to the customer.
The fulfilment status can be one of the following.
  • pending The campaign order has been created, but no fulfilment has been generated. For Shopify, this is an incredibly short-lived state.
  • on hold The campaign order has been created, and Shopify has placed the corresponding fulfilment on hold. The fulfilment process cannot progress until this hold has been released.
  • allocated The merchant has applied inventory to the campaign, and Submarine has allocated it to this campaign order.
    • Note that Submarine will only ever allocate the full amount of a campaign order. If the order has a quantity of three, for example, but the applied inventory only has two available, then the order will remain on hold and the next in line will be considered for allocation.
  • submitted Submarine has sent a request to Shopify to update the status of a fulfilment, and is waiting for the result.
  • open The Shopify fulfilment for the campaign order has been released and is now open.
  • fulfilled The Shopify fulfilment for the campaign order has been completed.
    • Note that it is not the responsibility of Submarine to complete open fulfilments. Submarine will listen for such events and will report back in the Submarine UI, but it will never trigger them.
  • failed A request to update the status of a Shopify fulfilment has been unsuccessful. Submarine will make an effort to retry the fulfilment, but both the customer and merchant can also manually trigger a retry.
  • returned As a result of the campaign order being cancelled, the Shopify fulfilment has been returned.