The SubscriptionContract model provides methods for querying and managing subscription contracts throughout their lifecycle, from creation through billing and fulfillment.
Overview
Subscription contracts represent active subscriptions between customers and merchants. This model handles:
Retrieving subscription contract lists and details
Accessing billing attempt information
Calculating price breakdowns
Managing contract lifecycle operations
Fetching contract data for rebilling
Core Methods
getContracts
Retrieves a paginated list of subscription contracts.
The authenticated GraphQL client instance
variables
SubscriptionContractsQueryVariables
required
Query parameters including filters and pagination Show variables properties
Number of contracts to retrieve
Cursor for forward pagination
Search query to filter contracts
Returns: Promise<{subscriptionContracts: SubscriptionContractListItem[], subscriptionContractPageInfo: PaginationInfo, hasContractsWithInventoryError: boolean}>
TypeScript Signature:
async function getContracts (
graphql : GraphQLClient ,
variables : SubscriptionContractsQueryVariables ,
) : Promise <{
subscriptionContracts : SubscriptionContractListItem [];
subscriptionContractPageInfo : PaginationInfo ;
hasContractsWithInventoryError : boolean ;
}>
Usage Example:
import { getContracts } from '~/models/SubscriptionContract/SubscriptionContract.server' ;
const {
subscriptionContracts ,
subscriptionContractPageInfo ,
hasContractsWithInventoryError
} = await getContracts ( graphql , {
first: 50 ,
query: 'status:ACTIVE' ,
});
console . log ( `Found ${ subscriptionContracts . length } active contracts` );
subscriptionContracts . forEach ( contract => {
console . log ( `Contract ${ contract . id } ` );
console . log ( `Customer: ${ contract . customer . displayName } ` );
console . log ( `Status: ${ contract . status } ` );
console . log ( `Total: ${ contract . totalPrice . amount } ${ contract . totalPrice . currencyCode } ` );
});
if ( hasContractsWithInventoryError ) {
console . warn ( 'Some contracts have inventory errors' );
}
GraphQL Query Used:
query SubscriptionContracts ( $first : Int , $after : String , $query : String ) {
subscriptionContracts ( first : $first , after : $after , query : $query ) {
edges {
node {
id
status
currencyCode
customer {
displayName
}
deliveryPolicy {
interval
intervalCount
}
lines ( first : 50 ) {
edges {
node {
id
title
variantTitle
quantity
productId
lineDiscountedPrice {
amount
}
}
}
}
linesCount {
count
}
billingAttempts ( first : 10 , reverse : true ) {
edges {
node {
id
errorCode
errorMessage
}
}
}
}
}
pageInfo {
hasNextPage
endCursor
}
}
contractsWithInventoryError : subscriptionContracts (
first : 1
query : "status:ACTIVE AND billingAttempt.errorCode:INVENTORY_ALLOCATIONS_NOT_FOUND"
) {
edges {
node {
id
}
}
}
}
getContractDetails
Retrieves comprehensive details for a single subscription contract.
The authenticated GraphQL client instance
The subscription contract ID (e.g., gid://shopify/SubscriptionContract/123)
Returns: Promise<SubscriptionContractDetails>
TypeScript Signature:
async function getContractDetails (
graphql : GraphQLClient ,
id : string ,
) : Promise < SubscriptionContractDetails >
Usage Example:
import { getContractDetails } from '~/models/SubscriptionContract/SubscriptionContract.server' ;
const contract = await getContractDetails (
graphql ,
'gid://shopify/SubscriptionContract/1'
);
console . log ( 'Contract Details:' );
console . log ( '- Customer:' , contract . customer ?. displayName );
console . log ( '- Email:' , contract . customer ?. email );
console . log ( '- Status:' , contract . status );
console . log ( '- Delivery Method:' , contract . deliveryMethod ?. name );
console . log ( '- Lines:' , contract . lines . length );
console . log ( '- Subtotal:' , contract . priceBreakdownEstimate . subtotalPrice . amount );
console . log ( '- Shipping:' , contract . priceBreakdownEstimate . totalShippingPrice . amount );
// Access customer addresses
contract . customer ?. addresses . forEach (({ id , address }) => {
console . log ( `Address ${ id } : ${ address } ` );
});
// Check billing attempts
console . log ( 'Recent billing attempts:' , contract . billingAttempts . length );
getContractEditDetails
Retrieves contract details optimized for editing operations.
The authenticated GraphQL client instance
The subscription contract ID
Returns: Promise<SubscriptionContractEditDetails>
TypeScript Signature:
async function getContractEditDetails (
graphql : GraphQLClient ,
id : string ,
) : Promise < SubscriptionContractEditDetails >
Usage Example:
import { getContractEditDetails } from '~/models/SubscriptionContract/SubscriptionContract.server' ;
const contract = await getContractEditDetails (
graphql ,
'gid://shopify/SubscriptionContract/1'
);
// Each line includes current one-time purchase price for comparison
contract . lines . forEach ( line => {
console . log ( ` ${ line . title } :` );
console . log ( `- Subscription price: ${ line . currentPrice . amount } ` );
console . log ( `- One-time price: ${ line . currentOneTimePurchasePrice } ` );
});
console . log ( 'Delivery Policy:' , contract . deliveryPolicy );
console . log ( 'Price Breakdown:' , contract . priceBreakdownEstimate );
Note: This method fetches the current one-time purchase price for each product variant, which is useful for displaying savings when editing a contract.
getContractDetailsForRebilling
Retrieves contract details specifically for rebilling operations.
The authenticated GraphQL client instance
The subscription contract ID
Returns: Promise<SubscriptionContract>
TypeScript Signature:
async function getContractDetailsForRebilling (
graphql : GraphQLClient ,
id : string ,
) : Promise < SubscriptionContract >
Usage Example:
import { getContractDetailsForRebilling } from '~/models/SubscriptionContract/SubscriptionContract.server' ;
try {
const contract = await getContractDetailsForRebilling (
graphql ,
'gid://shopify/SubscriptionContract/1'
);
console . log ( 'Contract ready for rebilling' );
console . log ( 'Customer ID:' , contract . customer ?. id );
console . log ( 'Next billing date:' , contract . nextBillingDate );
} catch ( error ) {
console . error ( 'Failed to load contract for rebilling:' , error );
}
findSubscriptionContractWithBillingCycle
Finds a subscription contract along with a specific billing cycle.
The shop domain (e.g., example.myshopify.com)
The subscription contract ID
The billing cycle date in ISO format (e.g., 2024-03-15)
Returns: Promise<{subscriptionContract: SubscriptionContract, subscriptionBillingCycle: SubscriptionBillingCycle}>
TypeScript Signature:
interface FindSubscriptionContractWithBillingCycleArgs {
shop : string ;
contractId : string ;
date : string ;
}
async function findSubscriptionContractWithBillingCycle ({
shop ,
contractId ,
date ,
} : FindSubscriptionContractWithBillingCycleArgs ) : Promise <{
subscriptionContract : SubscriptionContract ;
subscriptionBillingCycle : SubscriptionBillingCycle ;
}>
Usage Example:
import { findSubscriptionContractWithBillingCycle } from '~/models/SubscriptionContract/SubscriptionContract.server' ;
const { subscriptionContract , subscriptionBillingCycle } =
await findSubscriptionContractWithBillingCycle ({
shop: 'example.myshopify.com' ,
contractId: 'gid://shopify/SubscriptionContract/1' ,
date: '2024-03-15' ,
});
console . log ( 'Contract:' , subscriptionContract . id );
console . log ( 'Billing Cycle Index:' , subscriptionBillingCycle . cycleIndex );
console . log ( 'Billing Attempts:' , subscriptionBillingCycle . billingAttempts . edges . length );
// Access billing attempt details
subscriptionBillingCycle . billingAttempts . edges . forEach (({ node }) => {
console . log ( `Attempt ${ node . id } :` );
console . log ( `- Error Code: ${ node . errorCode } ` );
console . log ( `- Ready: ${ node . ready } ` );
});
GraphQL Query Used:
query SubscriptionContractWithBillingCycle ( $contractId : ID ! , $date : DateTime ! ) {
subscriptionContract ( id : $contractId ) {
id
status
customer {
id
displayName
}
}
subscriptionBillingCycle ( contractId : $contractId , date : $date ) {
cycleIndex
billingAttempts ( first : 10 ) {
edges {
node {
id
ready
errorCode
errorMessage
}
}
}
}
}
getContractCustomerId
Retrieves the customer ID associated with a subscription contract.
The subscription contract ID
Returns: Promise<string>
TypeScript Signature:
async function getContractCustomerId (
shopDomain : string ,
subscriptionContractId : string ,
) : Promise < string >
Usage Example:
import { getContractCustomerId } from '~/models/SubscriptionContract/SubscriptionContract.server' ;
const customerId = await getContractCustomerId (
'example.myshopify.com' ,
'gid://shopify/SubscriptionContract/1'
);
console . log ( 'Customer ID:' , customerId );
// Output: gid://shopify/Customer/123
getProductVariantPrice
Retrieves the current price for a product variant.
The authenticated GraphQL client instance
Returns: Promise<number>
TypeScript Signature:
async function getProductVariantPrice (
graphql : GraphQLClient ,
productVariantId : string ,
) : Promise < number >
Usage Example:
import { getProductVariantPrice } from '~/models/SubscriptionContract/SubscriptionContract.server' ;
const price = await getProductVariantPrice (
graphql ,
'gid://shopify/ProductVariant/123'
);
console . log ( 'Current variant price:' , price );
// Output: 29.99
TypeScript Interfaces
SubscriptionContractListItem
interface SubscriptionContractListItem {
id : string ;
status : SubscriptionContractStatusEnum ;
totalPrice : MoneyV2 ;
customer : {
displayName ?: string | null ;
};
deliveryPolicy ?: {
interval : string ;
intervalCount : number ;
} | null ;
billingAttempts ?: {
id : string ;
errorCode ?: string | null ;
processingError ?: string | null ;
}[];
lines : SubscriptionLineWithProductId [];
lineCount : number ;
}
SubscriptionContractDetails
interface SubscriptionContractDetails {
id : string ;
status : SubscriptionContractStatusEnum ;
customer ?: {
id : string ;
displayName ?: string | null ;
email ?: string | null ;
addresses : FormattedAddressWithId [];
};
deliveryMethod ?: {
__typename : string ;
address ?: {
address1 ?: string | null ;
address2 ?: string | null ;
city ?: string | null ;
province ?: string | null ;
country ?: string | null ;
zip ?: string | null ;
} | null ;
name : string ;
isLocalPickup : boolean ;
};
lines : SubscriptionLine [];
billingAttempts : BillingAttempt [];
lastPaymentStatus : SubscriptionContractLastPaymentStatusEnum | null ;
priceBreakdownEstimate : {
subtotalPrice : MoneyV2 ;
totalShippingPrice : MoneyV2 ;
};
}
SubscriptionContractEditDetails
interface SubscriptionContractEditDetails {
id : string ;
status : SubscriptionContractStatusEnum ;
lines : ( SubscriptionLine & {
currentOneTimePurchasePrice ?: number ;
})[];
deliveryMethod ?: {
name : string ;
isLocalPickup : boolean ;
};
deliveryPolicy : {
intervalCount : number ;
interval : string ;
};
priceBreakdownEstimate : {
subtotalPrice : MoneyV2 ;
totalShippingPrice : MoneyV2 ;
};
}
MoneyV2
interface MoneyV2 {
amount : number ;
currencyCode : string ;
}
interface FormattedAddressWithId {
id : string ;
address : string ; // Formatted address string
}
SubscriptionContractStatusEnum
enum SubscriptionContractStatusEnum {
ACTIVE = 'ACTIVE' ,
PAUSED = 'PAUSED' ,
CANCELLED = 'CANCELLED' ,
EXPIRED = 'EXPIRED' ,
FAILED = 'FAILED' ,
}
Helper Functions
getContractPriceBreakdown
Internal helper function that calculates price breakdowns for contracts.
function getContractPriceBreakdown ({
currencyCode ,
lines ,
discounts = [],
deliveryPrice = { amount: 0 },
} : {
currencyCode : string ;
lines : { lineDiscountedPrice : { amount : number } }[];
discounts ?: { targetType : string }[];
deliveryPrice ?: { amount : number };
}) : {
subtotalPrice : MoneyV2 ;
totalShippingPrice : MoneyV2 ;
}
This function:
Sums all line item prices to calculate the subtotal
Checks if shipping discounts are applied
Returns formatted price objects with currency codes