Skip to content

Commit

Permalink
feat: add 3DS session support and update to cards endpoint
Browse files Browse the repository at this point in the history
Includes card changes to use `multi_use` and new endpoint with /payments
prefix.

Add support for Create a 3DS session.

BREAKING CHANGE: requireda`multi_use` field when creating a card
  • Loading branch information
alan committed Nov 12, 2024
1 parent d241e4b commit 9ff22e7
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 5 deletions.
14 changes: 12 additions & 2 deletions src/Vault/Cards/Cards.spec.ts → src/Payments/Cards/Cards.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,17 @@ describe('Cards', () => {
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 } })
.post('/payments/cards')
.reply(200, {
data: {
id: MOCK_ID,
liveMode: false,
unavailableAt: '2024-01-01T00:00:00',
brand: 'visa',
multiUse: false,
last4Digits: '4242',
},
})

const response = await duffel.cards.create({
address_city: 'London',
Expand All @@ -25,6 +34,7 @@ describe('Cards', () => {
name: 'Neil Armstrong',
number: '4242424242424242',
cvc: '123',
multi_use: false,
})
expect(response.data.id).toBe(MOCK_ID)
})
Expand Down
10 changes: 9 additions & 1 deletion src/Vault/Cards/Cards.ts → src/Payments/Cards/Cards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,19 @@ interface CardParameters {
* The card verification code
*/
cvc: string
/**
* Set to true to indicate that this card can be used multiple times
*/
multi_use: boolean
}

interface CardRecord {
id: string
live_mode: boolean
multi_use: boolean
unavailble_at: string
brand: string
last_4_digits: string
}

export class Cards extends Resource {
Expand All @@ -72,7 +80,7 @@ export class Cards extends Resource {
// basePath must be 'https://api.duffel.cards'
constructor(client: Client) {
super(client)
this.path = 'vault/cards'
this.path = 'payments/cards'
}

/**
Expand Down
File renamed without changes.
34 changes: 34 additions & 0 deletions src/Payments/ThreeDSecureSessions/ThreeDSecureSessions.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import nock from 'nock'
import { Duffel } from '../../index'

const duffel = new Duffel({ token: 'mockToken' })
describe('ThreeDSecureSessions', () => {
afterEach(() => {
nock.cleanAll()
})

it('should create a 3DS session record when `create` is called', async () => {
const MOCK_ID = '3ds_00009hthhsUZ8W4LxQgkjb'
nock(/(.*)/)
.post('/payments/three_d_secure_sessions')
.reply(200, {
data: {
id: MOCK_ID,
liveMode: false,
expiresAt: '2024-01-01T00:00:00',
status: 'ready_for_payment',
resourceId: 'off_00009hthhsUZ8W4LxQgkjb',
clientId: 'tds_57aa862f8bf7',
cardId: 'tcd_00009hthhsUZ8W4LxQgkjb',
},
})

const response = await duffel.three_d_secure_sessions.create({
resource_id: 'off_00009hthhsUZ8W4LxQgkjb',
card_id: 'tcd_00009hthhsUZ8W4LxQgkjb',
services: [{ quantity: 1, id: 'ser_00009UhD4ongolulWd9123' }],
exception: 'secure_corporate_payment',
})
expect(response.data.id).toBe(MOCK_ID)
})
})
65 changes: 65 additions & 0 deletions src/Payments/ThreeDSecureSessions/ThreeDSecureSessions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { Resource } from '../../Resource'
import { DuffelResponse } from '../../types'

interface Service {
/**
* The quantity of the service ID to pay for
*/
quantity: number
/**
* The ID of the service to pay for
*/
id: string
}

interface ThreeDSecureSessionParameters {
/**
* The offer ID, order ID, order change ID or quote ID intended to pay
*/
resource_id: string

/**
* The services inteded to pay
*/
services: Service[]

/**
* The card ID
*/
card_id: string

/**
* The exception name for the 3DS session
*/
exception: string
}

interface ThreeDSecureSessionRecord {
id: string
resource_id: string
card_id: string
live_mode: boolean
expires_at: string
status: string
client_id: string
}

export class ThreeDSecureSessions extends Resource {
/**
* Endpoint path
*/
path: string

constructor(args: any) {
super(args)
this.path = 'payments/three_d_secure_sessions'
}

/**
* Create a Duffel ThreeDSecureSession record
*/
public create = async (
data: ThreeDSecureSessionParameters,
): Promise<DuffelResponse<ThreeDSecureSessionRecord>> =>
this.request({ method: 'POST', path: this.path, data })
}
1 change: 1 addition & 0 deletions src/Payments/ThreeDSecureSessions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './ThreeDSecureSessions'
2 changes: 2 additions & 0 deletions src/Payments/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './Cards'
export * from './ThreeDSecureSessions'
1 change: 0 additions & 1 deletion src/Vault/index.ts

This file was deleted.

6 changes: 5 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +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'
import { Cards } from './Payments/Cards'
import { ThreeDSecureSessions } from './Payments/ThreeDSecureSessions'

export interface DuffelAPIClient {
aircraft: Aircraft
Expand All @@ -34,6 +35,7 @@ export interface DuffelAPIClient {
orderCancellations: OrderCancellations
payments: Payments
seatMaps: SeatMaps
threeDSecureSessions: ThreeDSecureSessions
}

export class Duffel {
Expand All @@ -58,6 +60,7 @@ export class Duffel {
public refunds: Refunds
public webhooks: Webhooks
public stays: Stays
public three_d_secure_sessions: ThreeDSecureSessions

private cardsClient: Client
public cards: Cards
Expand Down Expand Up @@ -85,6 +88,7 @@ export class Duffel {
this.refunds = new Refunds(this.client)
this.webhooks = new Webhooks(this.client)
this.stays = new Stays(this.client)
this.three_d_secure_sessions = new ThreeDSecureSessions(this.client)

this.cardsClient = new Client({
...config,
Expand Down

0 comments on commit 9ff22e7

Please sign in to comment.