From 63c00a84dfc6bb60a705c6c055fa056c88b6ba85 Mon Sep 17 00:00:00 2001 From: Andreja Kogovsek Date: Wed, 15 Nov 2023 14:54:46 +0000 Subject: [PATCH 1/2] fix: typo --- src/Stays/Bookings/Bookings.ts | 4 ++-- src/Stays/StaysTypes.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Stays/Bookings/Bookings.ts b/src/Stays/Bookings/Bookings.ts index 45edc091..7e3f8b0e 100644 --- a/src/Stays/Bookings/Bookings.ts +++ b/src/Stays/Bookings/Bookings.ts @@ -65,10 +65,10 @@ export class Bookings extends Resource { * @param {string} bookingId - The ID of the booking */ public cancel = async ( - bookindId: string, + bookingId: string, ): Promise> => this.request({ method: 'POST', - path: `${this.path}/${bookindId}/actions/cancel`, + path: `${this.path}/${bookingId}/actions/cancel`, }) } diff --git a/src/Stays/StaysTypes.ts b/src/Stays/StaysTypes.ts index 063f18c8..264959de 100644 --- a/src/Stays/StaysTypes.ts +++ b/src/Stays/StaysTypes.ts @@ -252,7 +252,7 @@ export interface StaysAddress { postal_code: string /** - * The setay's region or state + * The stay's region or state */ region: string } From 9098621952dd50149cfb5c395ec1e28b2eb2a988 Mon Sep 17 00:00:00 2001 From: Andreja Kogovsek Date: Wed, 15 Nov 2023 14:55:39 +0000 Subject: [PATCH 2/2] feat: add cards tokenisation endpoint --- src/Vault/Cards/Cards.spec.ts | 31 +++++++++++++ src/Vault/Cards/Cards.ts | 85 +++++++++++++++++++++++++++++++++++ src/Vault/Cards/index.ts | 1 + src/Vault/index.ts | 1 + src/index.ts | 11 +++++ 5 files changed, 129 insertions(+) create mode 100644 src/Vault/Cards/Cards.spec.ts create mode 100644 src/Vault/Cards/Cards.ts create mode 100644 src/Vault/Cards/index.ts create mode 100644 src/Vault/index.ts diff --git a/src/Vault/Cards/Cards.spec.ts b/src/Vault/Cards/Cards.spec.ts new file mode 100644 index 00000000..e5318801 --- /dev/null +++ b/src/Vault/Cards/Cards.spec.ts @@ -0,0 +1,31 @@ +import nock from 'nock' +import { Duffel } from '../../index' + +const duffel = new Duffel({ token: 'mockToken' }) +describe('Cards', () => { + afterEach(() => { + nock.cleanAll() + }) + + it('should create a card record when `create` is called', async () => { + const MOCK_ID = 'tcd_00009hthhsUZ8W4LxQgkjb' + nock(/(.*)/) + .post('/vault/cards') + .reply(200, { data: { id: MOCK_ID, liveMode: false } }) + + const response = await duffel.cards.create({ + address_city: 'London', + address_country_code: 'GB', + address_line_1: '1 Downing St', + address_postal_code: 'EC2A 4RQ', + address_region: 'London', + brand: 'visa', + expiry_month: '03', + expiry_year: '30', + name: 'Neil Armstrong', + number: '4242424242424242', + cvc: '123', + }) + expect(response.data.id).toBe(MOCK_ID) + }) +}) diff --git a/src/Vault/Cards/Cards.ts b/src/Vault/Cards/Cards.ts new file mode 100644 index 00000000..07f02b5a --- /dev/null +++ b/src/Vault/Cards/Cards.ts @@ -0,0 +1,85 @@ +import { Client } from '../../Client' +import { Resource } from '../../Resource' +import { DuffelResponse } from '../../types' + +export type CardBrand = + | 'visa' + | 'mastercard' + | 'uatp' + | 'american_express' + | 'diners_club' + | 'jcb' + +interface CardParameters { + /** + * The first line of the card owner's address + */ + address_line_1: string + + /** + * The card owner's postal code (or zip code) + */ + address_postal_code: string + /** + * The card owner's city + */ + address_city: string + /** + * The card owner's region or state + */ + address_region: string + /** + * The ISO 3166-1 alpha-2 code for the card owner's country + */ + address_country_code: string + /** + * The brand or the type of the card + */ + brand: CardBrand + /** + * The month that the card expires in as a two-digit string, e.g. "01" + */ + expiry_month: string + /** + * The year that the card expires in as a two-digit string, e.g. "28" + */ + expiry_year: string + /** + * The card owner's name + */ + name: string + /** + * The card number + */ + number: string + /** + * The card verification code + */ + cvc: string +} + +interface CardRecord { + id: string + live_mode: boolean +} + +export class Cards extends Resource { + /** + * Endpoint path + */ + path: string + + // basePath must be 'https://api.duffel.cards' + constructor(client: Client) { + super(client) + this.path = 'vault/cards' + } + + /** + * Create a Duffel card record + */ + public create = async ( + data: CardParameters, + ): Promise> => + this.request({ method: 'POST', path: this.path, data }) +} diff --git a/src/Vault/Cards/index.ts b/src/Vault/Cards/index.ts new file mode 100644 index 00000000..90016471 --- /dev/null +++ b/src/Vault/Cards/index.ts @@ -0,0 +1 @@ +export * from './Cards' diff --git a/src/Vault/index.ts b/src/Vault/index.ts new file mode 100644 index 00000000..90016471 --- /dev/null +++ b/src/Vault/index.ts @@ -0,0 +1 @@ +export * from './Cards' diff --git a/src/index.ts b/src/index.ts index c47841f3..85859804 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,6 +19,8 @@ import { Refunds } from './DuffelPayments/Refunds' import { Sessions } from './Links' import { Webhooks } from './notifications' import { Stays } from './Stays/Stays' +import { Cards } from './Vault/Cards' + export interface DuffelAPIClient { aircraft: Aircraft airlines: Airlines @@ -57,6 +59,9 @@ export class Duffel { public webhooks: Webhooks public stays: Stays + private cardsClient: Client + public cards: Cards + constructor(config: Config) { this.client = new Client(config) @@ -80,6 +85,12 @@ export class Duffel { this.refunds = new Refunds(this.client) this.webhooks = new Webhooks(this.client) this.stays = new Stays(this.client) + + this.cardsClient = new Client({ + ...config, + basePath: 'https://api.duffel.cards', + }) + this.cards = new Cards(this.cardsClient) } }