# Subscription Service

If you only require Subscription API functionality, you can import the module separately to the main SDK.

For example:

```javascript
const { Subscription } = require("n-membership-sdk");
const subscriptionSdk = new Subscription(config);
```

## Table of Contents

- [Config](#config)
  - [subscriptionApiAppId](#subscriptionApiAppId)
  - [How to obtain a key](#how-to-obtain-a-key)
- [Methods](#methods)
  - [createSubscription](#createSubscription)
  - [fetchGatewayConfig](#fetchGatewayConfig)
  - [fetchGatewayName](#fetchGatewayName)
  - [validatePaymentSignature](#validatePaymentSignature)
  - [getSubscriptionHistoryByUserId](#getSubscriptionHistoryByUserId)
  - [getSubscriptionHistoryById](#getSubscriptionHistoryById)
  - [updateDefaultPaymentMethod](#updateDefaultPaymentMethod)

## Config

- See common configuration options in the [README file](./README.doc)
- `subscriptionApiKey`
- `subscriptionApiAppId`

```javascript
const config = new ProductionConfiguration({
  subscriptionApiKey: "secret",
  subscriptionApiAppId: "secretId",
});
```

### subscriptionApiAppId

This is only needed if you want to use the [fetchGatewayConfig](#fetchGatewayConfig) method.

### How to obtain a key

Do one of the following:

1. Check in Doppler for your app to see if a subscription API key already exists.
2. Contact #membership-support slack channel for a subscription service key.

## Methods

### createSubscription

```javascript
const createdUserSubscription = subscriptionSdk.createSubscription(
  user,
  paymentOptions,
  ravelinDeviceId,
  deliveryAddress
);
```

| Parameters      | Required | Type                                      | Example                          | Description                    |
| --------------- | -------- | ----------------------------------------- | -------------------------------- | ------------------------------ |
| user            | yes      | Object                                    | [User](../../src/models/user.ts) | User's details                 |
| paymentOptions  | yes      | [Object](../../src/types/subscription.ts) | see below                        | Payment details                |
| ravelinDeviceId | yes      | [Object](../../src/types/subscription.ts) | see below                        | Ravelin device tracking cookie |
| deliveryAddress | no       | [Object](../../src/types/subscription.ts) | see below                        | Delivery address               |

**Payment Options Object**:

```javascript
	{
		paymentType: 'CREDITCARD',
		paymentMethodId: 'xxxx',
		paymentGateway: 'WorldPay - GBP',
		paymentTerm: 'P1Y',
		billingCountry: 'GBP',
		offer: '6c7b7f36-0b91-b33b-2257-71d09919ddc4',
		option: 'HD',
		startDate: 'date',
		singleTermSubscription: false,
		closePaymentMethod: false,
		backdatedStartDate: '2022-12-01',
		sendConfirmationEmail: true // boolean, OPTIONAL. If not included in the Payment Options object, this will default to `true`
	}
```

**Delivery address Object**:
For UK

```javascript
	{
		addressLine1: 'Bracken House',
		addressLine2: '1 Friday St',
		addressLine3: '',
		city: 'London',
		countryCode: 'GBP',
		county: 'London',
		notes: 'leave at reception'
		postcode: 'EC4M 9BT',
		company: 'Company Name',
		poBox: false
	}
```

For USA/CAN

```javascript
	{
		addressLine1: '242 W 41st St',
		addressLine2: '',
		addressLine3: '',
		city: 'New York',
		countryCode: 'USA',
		notes: 'leave at reception'
		postcode: '10036',
		company: 'Company Name',
		poBox: false
	}
```

state just apply for the countries US and CAN otherwise should be undefined. When poBox is true, addressLine1 should contain the Po Box Number.

**Ravelin deviceId string**:  
Ravelin device id is generated by ravelin client javascript code and saved a a browser cookie.  
It's used to "fingerprint" users and help prevent fraud attempts.  
If not available, any non-null random string should work against test environment.

```javascript
ravelinDeviceId: "xxxxxx-xxxxx-xxxxx-xxxx-xxxxxxxxxx";
```

**Response**:

```javascript
	{
		subscriptionId: 'xxxx',
		subscriptionNumber: 'xxxx',
		invoiceId: 'xxxx',
		invoiceNumber: 'xxxx',
		paymentId: 'xxxx'
	}
```

### fetchGatewayConfig

```javascript
const gatewayConfig = subscriptionSdk.fetchGatewayConfig(configOptions);
```

| Parameters    | Required | Type                      | Example | Description                                                                                                                                                                         |
| ------------- | -------- | ------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| configOptions | yes      | fetchGatewayConfigOptions |         | configOptions is a required argument allowed for extension in case more values need to be passed to fetchGatewayConfig in the future. Please see below for allowed key value pairs. |

#### configOptions

```javascript
const configOptions = {
  paymentType: "cc",
  paymentTerm: "P1M",
  countryCode: "GBR",
  clientIPAddress: "123.0.0.1",
  authorizationAmount: 0,
  appIdOverride: "NEXTAPP",
  userId: "6c7b7f36-0b91-xxxx",
  ftSession: "ft-session-s-secure-token",
};
```

| Parameters          | Required | Type   | Example                                | Description                                                                                                                               |
| ------------------- | -------- | ------ | -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| paymentType         | yes      | String | cc                                     | Type of payment being used                                                                                                                |
| paymentTerm         | yes      | String | P1M                                    | Payment term code, P1M/P1Y/etc. Also known as billingFrequency, returned as `iso8601Duration` in the offer pricing response from FT Core. |
| countryCode         | yes      | String | GBR                                    | Three letter ISO 3166-1 country code                                                                                                      |
| clientIPAddress     | yes      | String | '34.567.0.23'                          | Client's IP address (IPv4 or IPv6)                                                                                                        |
| authorizationAmount | yes      | Number | 1                                      | Authorization amount, automatically voided unless used in conjunction with 3D Secure                                                      |
| appIdOverride       | no       | String | NEXTAPP                                | If provided, replaces the appId provided via config                                                                                       |
| userId              | yes      | String | '6c7b7f36-0b91-b33b-2257-71d09919ddc4' | The user ID, used to look up which currency the customer is using                                                                         |
| ftSession           | no       | String | value of FTSession_s                   | The user session, used to perform security checks                                                                                         |

**Response**:
[PaymentGatewayResponse](../../src/types/response.ts)
[PassthroughFieldsResponse](../../src/types/response.ts)

```json
{
  "id": "xxxx",
  "key": "xxxx",
  "style": "inline",
  "token": "xxxx",
  "is3DS": "false",
  "tenantId": "xxxx",
  "signature": "xxxxx",
  "submitEnabled": "xxxx",
  "paymentGateway": "WorldPay - GBP",
  "url": "https://xxxx",
  "field_currency": "GBP",
  "field_passthrough1": "xxxx",
  "field_passthrough2": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "field_passthrough3": "NEXTSUBSCRIBE",
  "field_passthrough4": "WorldPay - GBP",
  "field_passthrough5": "ft-secure-session",
  "field_passthrough6": "GBR",
  "field_passthrough7": "GBP",
  "authorizationAmount": 1,
  "param_gwOptions_purchaseTotals_currency": "GBP",
  "param_gwOptions_customer_ip": "xxx-xxx-xxx-xxx",
  "useZeroAuthorizationAmount": "false"
}
```

#### Zuora documentation

_Official Zuora Developer API Reference_
https://www.zuora.com/developer/api-reference/#operation/POST_CreateAuthorization

_field_currency_
https://knowledgecenter.zuora.com/Billing/Billing_and_Payments/LA_Hosted_Payment_Pages/B_Payment_Pages_2.0/J_Client_Parameters_for_Payment_Pages_2.0#Gateway_Options  
The currency in which the initial authorization should be done. Specify a 3-letter ISO currency code.
This value overrides the default currency in the tenant when creating a payment method without a Billing Account.
If field_accountId is set, then the currency on the Account is used, regardless of the value passed here.
If both "field_currency" and "param_gwOptions_purchaseTotals_currency" are given, the "param_gwOptions_purchaseTotals_currency" parameter takes precedence over "field_currency."

**this is inaccurate, some gateways use `field_currency` ONLY, so we need to set BOTH `param_gwOptions_purchaseTotals_currency` and `field_currency`**

_Authorization amount_
https://knowledgecenter.zuora.com/Billing/Billing_and_Payments/LA_Hosted_Payment_Pages/B_Payment_Pages_2.0/J_Client_Parameters_for_Payment_Pages_2.0#Gateway_Options  
The initial authorization amount.
Specify a numeric value greater than 0.
This value overrides the default authorization amount set for the specific payment gateway.
This amount will be automatically voided unless used in conjunction with 3D Secure.

https://knowledgecenter.zuora.com/Billing/Billing_and_Payments/LA_Hosted_Payment_Pages/B_Payment_Pages_2.0/Configure_Advanced_Security_Checks_for_Payment_Pages_2.0/3D_Secure_for_Payment_Pages_2.0  
You have to specify a value for the authorizationAmount field, the client-side HPM parameter in the request for rendering Payment Pages 2.0. If the value is not specified or equals 0, Zuora will skip the 3D Secure process.

### fetchGatewayName

```javascript
const gatewayName = subscriptionSdk.fetchGatewayName(paymentType, countryCode);
```

| Parameters  | Required | Type   | Example | Description                          |
| ----------- | -------- | ------ | ------- | ------------------------------------ |
| paymentType | yes      | String | cc      | Type of payment being used           |
| countryCode | yes      | String | GBR     | Three letter ISO 3166-1 country code |

**Response**: `WorldPay - GBP`

### validatePaymentSignature

```javascript
const validPaymentSignature =
  subscriptionSdk.validatePaymentSignature(signatureOptions);
```

#### signatureOptions

| Parameters       | Required | Type   | Example                                             | Description                                                     |
| ---------------- | -------- | ------ | --------------------------------------------------- | --------------------------------------------------------------- |
| signatureOptions | yes      | Object | [PaymentSignature](../../src/types/subscription.ts) | { token, pageId, tenantId, signature, paymentGateway, ?userId } |

```javascript
const signatureOptions = {
  token: "xxxx",
  pageId: "xxxx",
  tenantId: "xxxx",
  signature: "xxxxx",
  paymentGateway: "WorldPay - GBP",
  userId: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
};
```

Note: userId is optional but recommended as it's not used by membership to validate the signature but it could be used for rate limiting/logging purposes and log reconciliation.

**Response**:

```
	{
		status: 200,
		ok: true
	}
```

### getSubscriptionHistoryByUserId

```javascript
const subscriptionHistory =
  subscriptionSdk.getSubscriptionHistoryByUserId(uuid);
```

| Parameters | Required | Type   | Example   | Description |
| ---------- | -------- | ------ | --------- | ----------- |
| uuid       | yes      | String | eudmuS... | User's id   |

**Response**:

```
	{
		isInStepUp: true,
		stepUpEvent: SubscriptionHistoryModel | undefined,
		freezeEvent: SubscriptionHistoryModel | undefined,
		history: [{
			eventType: 'StepUp'
		}]
	}
```

### getSubscriptionHistoryById

```javascript
const subscriptionHistory =
  subscriptionSdk.getSubscriptionHistoryById(subscriptionNumber);
```

| Parameters         | Required | Type   | Example   | Description     |
| ------------------ | -------- | ------ | --------- | --------------- |
| subscriptionNumber | yes      | String | A-S000... | Subscription ID |

**Response**:

```
	{
		isInStepUp: true,
		stepUpEvent: SubscriptionHistoryModel | undefined,
		freezeEvent: SubscriptionHistoryModel | undefined,
		history: [{
			eventType: 'Creation'
		}]
	}
```

Each item in the history response array is an instance of the [SubscriptionHistoryModel](../SUBSCRIPTION-HISTORY.md)

### updateDefaultPaymentMethod

```javascript
subscriptionSdk.updateDefaultPaymentMethod(userId, paymentMethodId);
```

| Parameters      | Required | Type    | Example   | Description                            |
| --------------- | -------- | ------- | --------- | -------------------------------------- |
| userID          | yes      | String  | uuid      | User's ID                              |
| paymentMethodId | yes      | String  | unique id | Returned payment method ID             |
| paymentGateway  | no       | String  | WorldPay  | The name of the payment gateway to use |
| isPayNow        | no       | boolean | true      | if the user is paying now              |

### createPaymentMethod

```javascript
subscriptionSdk.createPaymentMethod(userDetails, paymentDetails);
```

| Parameters     | Required | Type   | Example   | Description                           |
| -------------- | -------- | ------ | --------- | ------------------------------------- |
| userDetails    | yes      | Object | uuid      | User's ID, email and countryCode      |
| paymentDetails | yes      | Object | unique id | paypalBAID, paypalEmail, currencyCode |
