# Recurring payments

Perform merchant-initiated or card-on-file transactions.

## Overview

Card components support two types of recurring payments:

* **Merchant-initiated transactions (MITs):** For subscription-based payments where the customer signs up and pays initially, and you then automatically charge them at a specified interval. This means there's no customer action needed for renewals.
* **Card-on-file transactions with shopper consent:** For storing payment methods with customer consent for future use. This allows you to provide a faster checkout experience for returning customers.


## Merchant-initiated

### Step 1: Set up the SDK config

To set up a recurring payment, you need to include the `recurring` object in your transaction data of your `sdkConfig`. You'll need to specify the frequency of this recurring payment (in days), but the expiration data is optional.


```typescript
// Configure subscription transaction data
const transactionData = {
  amount: 9.99,
  currency: 'USD',
  entryType: 'Ecom',
  intent: {
    card: 'Authorisation'
  },
  merchantTransactionId: 'sub-setup-123',
  merchantTransactionDate: () => new Date().toISOString(),
  recurring: {
    frequencyInDays: 30,
    frequencyExpiration: '2025-12-31'
  }
};

// Create complete SDK configuration
const sdkConfig = {
  transactionData,           // ← This connects Step 1 to Step 2
  apiKey: 'your-api-key',
  environment: 'sandbox',
  // ... other SDK settings
};
```

### Step 2: Create your components

Next, you're going to use your `sdkConfig` to create the relevant components. When the customer clicks submit, the recurring payments will start.


```typescript
const cardNumberComponent = new CardNumberComponent(sdkConfig);
const cardExpiryComponent = new CardExpiryDateComponent(sdkConfig);
const cardCvcComponent = new CardCvcComponent(sdkConfig);

const cardSubmitComponent = new CardSubmitComponent(sdkConfig, {
  cardNumberComponent,
  cardExpiryDateComponent: cardExpiryComponent,
  cardCvcComponent,
  useCardOnFile: false,
  submitText: 'Start subscription',
  onPostAuthorisation: async (data) => {
    console.log('Subscription setup completed');
    
    // Get full result from backend
    const result = await getAuthorisationResultFromGateway(data.merchantTransactionId, data.systemTransactionId);
    
    if (result.state === 'Authorised' || result.state === 'Captured') {
      console.log('Subscription started!');
      // The recurring data from step 1 was automatically used
      // Store the subscription details in your system
    }
  }
});

// Mount components
cardNumberComponent.mount('card-number');
cardExpiryComponent.mount('card-expiry');
cardCvcComponent.mount('card-cvc');
cardSubmitComponent.mount('submit-button');
```

## Card-on-file

### Step 1: Set up the initial transaction

To save a card, you'll first need to ask for the customer's consent during the initial transaction. If the customer ticks the checkbox, their card details will be saved as a token for future reuse.


```typescript
const transactionData = {
  amount: 99.99,
  currency: 'USD',
  entryType: 'Ecom',
  intent: {
    card: 'Authorisation'
  },
  merchantTransactionId: 'order-12345',
  merchantTransactionDate: () => new Date().toISOString()
};

const sdkConfig = {
  transactionData,
  // ... other SDK configuration
};

// Create the card consent component
const cardConsentComponent = new CardConsentComponent(sdkConfig, {
  label: 'Save this card for faster checkout in the future',
  class: 'consent-checkbox'
});

// Create the card input components
const cardNumberComponent = new CardNumberComponent(sdkConfig);
const cardExpiryComponent = new CardExpiryDateComponent(sdkConfig);
const cardCvcComponent = new CardCvcComponent(sdkConfig);

// Create the card submit component
const cardSubmitComponent = new CardSubmitComponent(sdkConfig, {
  cardNumberComponent,
  cardExpiryDateComponent: cardExpiryComponent,
  cardCvcComponent,
  cardConsentComponent, // Include consent component
  useCardOnFile: false, // Use a new card, not a saved card
  submitText: 'Pay & save card',
  onPostTokenisation: async (data) => {
    console.log('Card tokenised. Gateway Token ID:', data.gatewayTokenId);
    
    // Get full token details from backend if needed for validation
    const tokenDetails = await getTokenDetailsFromBackend(data.gatewayTokenId);
    console.log('Card saved for future use');
    
    // Store the token reference in your system
  },
  onPostAuthorisation: async (data) => {
    console.log('Payment completed');
    
    // Get full result from backend
    const result = await getAuthorisationResultFromGateway(data.merchantTransactionId, data.systemTransactionId);
    
    if (result.state === 'Authorised' || result.state === 'Captured') {
      console.log('Payment successful, card saved for future use');
    }
  }
});

// Mount components
cardNumberComponent.mount('card-number');
cardExpiryComponent.mount('card-expiry');
cardCvcComponent.mount('card-cvc');
cardConsentComponent.mount('card-consent');
cardSubmitComponent.mount('submit-button');
```

### Step 2: Set up subsequent transactions

For subsequent transactions, you can use choose from:

* **A traditional card selection:**
This is best for customers with multiple saved cards and provides
full card management (edit, delete, update) but requires more interaction from the customer. It uses the card-on-file component.
* **A streamlined click-once checkout:** This is ideal for customers with one primary payment method and offers a faster checkout experience, but less flexibility. It uses the standalone click-once component.
* **A hybrid approach:** This combines the two components. It defaults to displaying a primary card, but allows customers to choose a different one if they want to.



```typescript Card-on-file
const cardOnFileComponent = new CardOnFileComponent(sdkConfig, {
  limitTokens: 10,
  orderBy: {
    lastUsageDate: { direction: 'desc', priority: 1 }
  },
  deleteCardButtonAriaLabel: 'Delete this card',
  editCardInformationAriaLabel: 'Edit card details'
});
```


```typescript Click-once standalone
const clickOnceComponent = new ClickOnceStandaloneComponent(sdkConfig, {
  limitTokens: 1,
  isCvcRequired: false, 
  submitText: 'Complete purchase',
  disableCardSelection: true
});
```


```typescript Hybrid
// Primary checkout with click-once
const quickCheckout = new ClickOnceStandaloneComponent(sdkConfig, {
  limitTokens: 1,
  submitText: 'Pay with Primary Card',
  class: 'primary-checkout'
});

// Alternative with full card selection
const alternativeCheckout = new CardOnFileComponent(sdkConfig, {
  limitTokens: 5,
  class: 'alternative-checkout hidden' // Initially hidden
});

// Toggle between interfaces
const showAlternativeButton = document.createElement('button');
showAlternativeButton.textContent = 'Use different card';
showAlternativeButton.onclick = () => {
  document.querySelector('.primary-checkout').classList.add('hidden');
  document.querySelector('.alternative-checkout').classList.remove('hidden');
};
```