# Extension Subscription Gateway Flows

This document provides a detailed explanation of the extension subscription gateway flows, including the purpose of the flow, the services involved, and the description of each field in the requests and responses.

{% hint style="warning" %}
All requests are sent with `x-akinon-api-version: 2`. The microservice must validate this header and respond accordingly.
{% endhint %}

**Example configuration:**

```json
{
  "extension": {
    "klass": "omnishop.libs.subscription_gateways.extension.gateway.ExtensionGateway",
    "dispatch_klass": "omnishop.libs.subscription_gateways.service.GenericDispatchService",
    "conf": {
      "auth": {
        "username": "<basic-auth-username>",
        "password": "<basic-auth-password>"
      },
      "base_url": "https://extension-service.example.com/"
    }
  }
}
```

## Purpose of the Flow

The Extension Subscription Gateway Flow is designed to decouple Commerce from subscription providers (e.g. Emarsys, Revotas). Instead of communicating directly with each provider, Commerce sends user and order data to an external microservice, which is responsible for forwarding it to the configured provider. This flow is particularly useful for:

* **Provider Abstraction:** Allowing the microservice to handle provider-specific logic without changes to Commerce.
* **Centralized Subscription Management:** Supporting multiple providers through a single integration point.
* **Scalability:** Standardizing the integration with multiple third-party systems using a consistent API pattern.

This part of the document contains the list of services that can be integrated in flows above. 2 configuration parameters will be shared during integration:

| Field    | Description                       |
| -------- | --------------------------------- |
| username | Username for basic authentication |
| password | Password for basic authentication |

## <mark style="color:red;">subscribe-email (POST)</mark>

This service is used to subscribe a user by email address. It is triggered when a user opts in to email communication.

**URL:**

```
^^/subscribe-email
```

**Request Headers**

| Header               | Description                                 | Requirement |
| -------------------- | ------------------------------------------- | ----------- |
| x-akinon-request-id  | Unique ID for problem solving & tracing.    | Mandatory   |
| x-akinon-api-version | Akinon Customer API version.                | Mandatory   |
| Authorization        | Basic \[BASE64\_encoded(username:password)] | Mandatory   |

**Request Body Structure**

| Field  | Type   | Description                              |
| ------ | ------ | ---------------------------------------- |
| email  | String | Email address to subscribe               |
| domain | String | Domain from which the request originated |

**Request Body Example**

```json
{
  "email": "user@example.com",
  "domain": "example.com"
}
```

**Request Body (typescript interface)**

```ts
interface SubscribeEmailRequest {
  email: string;
  domain: string;
}
```

**Response Body (raw 200)**

```
No Body
```

### <mark style="color:red;">subscribe-user (POST)</mark>

This service is used to subscribe a user with full profile information. It is triggered when a user registers or when their subscription preferences are updated.

**URL:**

```
^^/subscribe-user
```

**Request Headers**

| Header               | Description                                 | Requirement |
| -------------------- | ------------------------------------------- | ----------- |
| x-akinon-request-id  | Unique ID for problem solving & tracing.    | Mandatory   |
| x-akinon-api-version | Akinon Customer API version.                | Mandatory   |
| Authorization        | Basic \[BASE64\_encoded(username:password)] | Mandatory   |

**Request Body Structure**

| Field           | Type                   | Description                        |
| --------------- | ---------------------- | ---------------------------------- |
| pk              | Number                 | User ID                            |
| email           | String                 | Email address                      |
| firstName       | String                 | First name                         |
| lastName        | String                 | Last name                          |
| phone           | String/Null            | Phone number                       |
| gender          | String/Null            | Gender                             |
| dateOfBirth     | String/Null            | Date of birth                      |
| languageCode    | String                 | Language code (e.g. `tr`, `en`)    |
| emailAllowed    | Boolean                | Email subscription consent         |
| smsAllowed      | Boolean                | SMS subscription consent           |
| callAllowed     | Boolean                | Call subscription consent          |
| whatsappAllowed | Boolean                | WhatsApp subscription consent      |
| isActive        | Boolean                | Whether the account is active      |
| isStaff         | Boolean                | Whether the user is a staff member |
| dateJoined      | String (ISO 8601)      | Registration date                  |
| lastLogin       | String/Null (ISO 8601) | Last login date                    |
| modifiedDate    | String (ISO 8601)      | Last modified date                 |
| userType        | String                 | User type                          |
| attributes      | Object                 | Additional user attributes         |

**Request Body Example**

```json
{
  "pk": 1,
  "username": "username",
  "firstName": "Test",
  "lastName": "User",
  "email": "user@example.com",
  "phone": "05351234567",
  "gender": "M",
  "dateOfBirth": "1990-01-01",
  "languageCode": "tr",
  "emailAllowed": true,
  "smsAllowed": true,
  "callAllowed": true,
  "whatsappAllowed": false,
  "isActive": true,
  "isStaff": false,
  "userType": "registered",
  "attributes": {
    "logged_ip": "127.0.0.1"
  },
  "dateJoined": "2024-01-01T00:00:00Z",
  "lastLogin": "2024-06-01T00:00:00Z",
  "modifiedDate": "2024-06-01T00:00:00Z"
}
```

**Request Body (typescript interface)**

```ts
interface SubscribeUserRequest {
  pk: number;
  email: string;
  firstName: string;
  lastName: string;
  phone: string | null;
  gender: string | null;
  dateOfBirth: string | null;
  languageCode: string;
  emailAllowed: boolean;
  smsAllowed: boolean;
  callAllowed: boolean;
  whatsappAllowed: boolean;
  isActive: boolean;
  isStaff: boolean;
  dateJoined: string;
  lastLogin: string | null;
  modifiedDate: string;
  userType: string;
  attributes: Record<string, any>;
}
```

**Response Body (raw 200)**

```
Nno Body
```

### <mark style="color:red;">order-info (POST)</mark>

This service is used to notify the microservice about a newly created order. It sends the full order details including the user profile and order items.

**URL:**

```
^^/order-info
```

**Request Headers**

| Header               | Description                                 | Requirement |
| -------------------- | ------------------------------------------- | ----------- |
| x-akinon-request-id  | Unique ID for problem solving & tracing.    | Mandatory   |
| x-akinon-api-version | Akinon Customer API version.                | Mandatory   |
| Authorization        | Basic \[BASE64\_encoded(username:password)] | Mandatory   |

**Request Body Structure**

| Field                       | Type              | Description                                                   |
| --------------------------- | ----------------- | ------------------------------------------------------------- |
| pk                          | Number            | Order ID                                                      |
| number                      | String            | Order number                                                  |
| status                      | String            | Order status name (e.g. `approved`, `shipped`, `delivered`)   |
| currency                    | String            | Currency code — ISO 4217 lowercase (e.g. `try`, `eur`, `usd`) |
| amount                      | String            | Total order amount (decimal value as string)                  |
| discountAmount              | String            | Total discount amount (decimal value as string)               |
| shippingAmount              | String            | Shipping cost (decimal value as string)                       |
| createdDate                 | String (ISO 8601) | Order creation date                                           |
| user                        | Object            | User profile — same structure as `/subscribe-user`            |
| orderItems                  | Array             | List of order items                                           |
| orderItems\[].pk            | Number            | Order item ID                                                 |
| orderItems\[].product       | Object            | Product info                                                  |
| orderItems\[].product.pk    | Number            | Product ID                                                    |
| orderItems\[].product.sku   | String            | Product SKU                                                   |
| orderItems\[].price         | String            | Unit price (decimal value as string)                          |
| orderItems\[].priceCurrency | String            | Currency code — ISO 4217 lowercase (e.g. `try`, `eur`, `usd`) |
| orderItems\[].taxRate       | String            | Tax rate (decimal value as string)                            |
| orderItems\[].status        | String            | Order item status name (e.g. `approved`, `shipped`)           |

**Request Body Example**

```json
{
  "pk": 1,
  "number": "ORD-0001",
  "status": "approved",
  "currency": "try",
  "amount": "199.99",
  "discountAmount": "10.00",
  "shippingAmount": "0.00",
  "createdDate": "2024-01-01T00:00:00Z",
  "user": {
    "pk": 1,
    "username": "username",
    "firstName": "Test",
    "lastName": "User",
    "email": "user@example.com",
    "phone": "05351234567",
    "gender": "M",
    "dateOfBirth": "1990-01-01",
    "languageCode": "tr",
    "emailAllowed": true,
    "smsAllowed": true,
    "callAllowed": true,
    "whatsappAllowed": false,
    "isActive": true,
    "isStaff": false,
    "userType": "registered",
    "attributes": {
      "logged_ip": "127.0.0.1"
    },
    "dateJoined": "2024-01-01T00:00:00Z",
    "lastLogin": "2024-06-01T00:00:00Z",
    "modifiedDate": "2024-06-01T00:00:00Z"
  },
  "orderItems": [
    {
      "pk": 1,
      "product": {
        "pk": 10,
        "sku": "SKU-001"
      },
      "price": "99.99",
      "priceCurrency": "try",
      "taxRate": "18.00",
      "status": "approved"
    }
  ]
}
```

**Request Body (typescript interface)**

```ts
interface OrderInfoRequest {
  pk: number;
  number: string;
  status: string;
  currency: string;
  amount: string;
  discountAmount: string;
  shippingAmount: string;
  createdDate: string;
  user: SubscribeUserRequest;
  orderItems: OrderItem[];
}

interface OrderItem {
  pk: number;
  product: {
    pk: number;
    sku: string;
  };
  price: string;
  priceCurrency: string;
  taxRate: string;
  status: string;
}
```

**Response Body (raw 200)**

```
No Body
```

### <mark style="color:red;">unsubscribed-users (GET)</mark>

This service is called by a scheduled Celery task every hour. Commerce fetches users who unsubscribed within the given time range and updates their consent fields (`email_allowed`, `sms_allowed`, `call_allowed`) accordingly. Only users with the relevant consent field currently set to `true` are affected.

**URL:**

```
^^/unsubscribed-users
```

**Request Headers**

| Header               | Description                                 | Requirement |
| -------------------- | ------------------------------------------- | ----------- |
| x-akinon-request-id  | Unique ID for problem solving & tracing.    | Mandatory   |
| x-akinon-api-version | Akinon Customer API version.                | Mandatory   |
| Authorization        | Basic \[BASE64\_encoded(username:password)] | Mandatory   |

**Query Parameters**

| Parameter | Type              | Description             |
| --------- | ----------------- | ----------------------- |
| fromDate  | String (ISO 8601) | Start of the time range |
| toDate    | String (ISO 8601) | End of the time range   |

**Response Body Structure**

| Field             | Type      | Description                                          |
| ----------------- | --------- | ---------------------------------------------------- |
| emailUnsubscribed | String\[] | Email addresses of users who unsubscribed from email |
| smsUnsubscribed   | String\[] | Phone numbers of users who unsubscribed from SMS     |
| callUnsubscribed  | String\[] | Phone numbers of users who unsubscribed from calls   |

**Response Body Example**

```json
{
  "emailUnsubscribed": ["user@example.com"],
  "smsUnsubscribed": ["05351234567"],
  "callUnsubscribed": ["05359999999"]
}
```

**Response Body (typescript interface)**

```ts
interface UnsubscribedUsersResponse {
  email_unsubscribed: string[];
  sms_unsubscribed: string[];
  call_unsubscribed: string[];
}
```

**Response Body For Errors (ALL)**

If any errors are encountered in the services, these errors should be reported in the format below, and the list of error codes should be shared with Akinon.

* **Response Body (raw 4xx/5xx json)**

```json
{
  "errors": [
    {
      "code": "",
      "field": "",
      "message": ""
    }
  ]
}
```

* **Response Body (raw 4xx/5xx typescript interface)**

```ts
interface ErrorResponse {
  errors: ErrorItem[];
}

interface ErrorItem {
  code: string;
  field: string;
  message: string;
}
```

## <mark style="color:red;">Changelog</mark>

### Version 1.0.0

#### Added

* Initial implementation of the Extension Subscription Gateway
* `subscribe-email` endpoint for email-only subscriptions
* `subscribe-user` endpoint for full user profile subscriptions
* `order-info` endpoint for order creation notifications
* `unsubscribed-users` endpoint for hourly consent synchronization
* HTTP Basic Auth support
* `x-akinon-request-id` and `x-akinon-api-version` headers on all requests


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://apidocs.akinon.com/flows/extension-subscription-gateway-flows.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
