Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Payment methods 2.0 #69

Merged
merged 3 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ When we make [non-breaking changes](https://developer.paddle.com/api-reference/a

This means when upgrading minor versions of the SDK, you may notice type errors. You can safely ignore these or fix by adding additional type guards.

## 2.0.0-rc.2 - 2024-11-19

### Added

- Added the `trafficSource` filter on notification settings
- Omitted the `transactionId` completely from `SubscriptionNotification` and created a separate `SubscriptionCreatedNotification` with the non-null `transactionId`
- Added `paymentMethods` resources
- Added `generateAuthToken` for customer

## 2.0.0-rc.1 - 2024-10-08

### Added
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@paddle/paddle-node-sdk",
"version": "2.0.0-rc.1",
"version": "2.0.0-rc.2",
"description": "A Node.js SDK that you can use to integrate Paddle Billing with applications written in server-side JavaScript.",
"main": "dist/cjs/index.cjs.node.js",
"module": "dist/esm/index.esm.node.js",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* ! Autogenerated code !
* Do not make changes to this file.
* Changes may be overwritten as part of auto-generation.
*/

import { IPaymentMethodDeletedNotificationResponse } from '../../../notifications/index.js';
import { type IEventsResponse } from '../../../types/index.js';

export const PaymentMethodDeletedMock: IEventsResponse<IPaymentMethodDeletedNotificationResponse> = {
event_id: 'evt_01hwz6k64a210xcvsdbg3y4vmr',
event_type: 'payment_method.deleted',
occurred_at: '2024-05-03T12:24:18.826338Z',
notification_id: 'ntf_01hwz6k66fp6cxtxyt6551wv7z',
data: {
id: 'paymtd_01hs8zx6x377xfsfrt2bqsevbw',
customer_id: 'ctm_01hv6y1jedq4p1n0yqn5ba3ky4',
address_id: 'add_01hv8gq3318ktkfengj2r75gfx',
deletion_reason: 'api',
type: 'card',
origin: 'saved_during_purchase',
saved_at: '2024-05-02T02:55:25.198953Z',
updated_at: '2024-05-03T12:24:18.826338Z',
},
};

export const PaymentMethodDeletedMockExpectation = {
data: {
id: 'paymtd_01hs8zx6x377xfsfrt2bqsevbw',
customerId: 'ctm_01hv6y1jedq4p1n0yqn5ba3ky4',
addressId: 'add_01hv8gq3318ktkfengj2r75gfx',
deletionReason: 'api',
type: 'card',
origin: 'saved_during_purchase',
savedAt: '2024-05-02T02:55:25.198953Z',
updatedAt: '2024-05-03T12:24:18.826338Z',
},
eventId: 'evt_01hwz6k64a210xcvsdbg3y4vmr',
eventType: 'payment_method.deleted',
notificationId: 'ntf_01hwz6k66fp6cxtxyt6551wv7z',
occurredAt: '2024-05-03T12:24:18.826338Z',
};
39 changes: 39 additions & 0 deletions src/__tests__/mocks/notifications/payment-method-saved.mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* ! Autogenerated code !
* Do not make changes to this file.
* Changes may be overwritten as part of auto-generation.
*/

import { type IEventsResponse, type IPaymentMethodResponse } from '../../../types/index.js';

export const PaymentMethodSavedMock: IEventsResponse<IPaymentMethodResponse> = {
event_id: 'evt_01hwvkmsge7bhq1a31s35784zt',
event_type: 'payment_method.saved',
occurred_at: '2024-05-02T02:55:25.198953Z',
notification_id: 'ntf_01hwvkmsknrgqw4z1598qw4ypt',
data: {
id: 'paymtd_01hs8zx6x377xfsfrt2bqsevbw',
customer_id: 'ctm_01hv6y1jedq4p1n0yqn5ba3ky4',
address_id: 'add_01hv8gq3318ktkfengj2r75gfx',
type: 'card',
origin: 'saved_during_purchase',
saved_at: '2024-05-02T02:55:25.198953Z',
updated_at: '2024-05-02T02:55:25.198953Z',
},
};

export const PaymentMethodSavedMockExpectation = {
data: {
id: 'paymtd_01hs8zx6x377xfsfrt2bqsevbw',
customerId: 'ctm_01hv6y1jedq4p1n0yqn5ba3ky4',
addressId: 'add_01hv8gq3318ktkfengj2r75gfx',
type: 'card',
origin: 'saved_during_purchase',
savedAt: '2024-05-02T02:55:25.198953Z',
updatedAt: '2024-05-02T02:55:25.198953Z',
},
eventId: 'evt_01hwvkmsge7bhq1a31s35784zt',
eventType: 'payment_method.saved',
notificationId: 'ntf_01hwvkmsknrgqw4z1598qw4ypt',
occurredAt: '2024-05-02T02:55:25.198953Z',
};
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,6 @@ export const SubscriptionActivatedMockExpectation = {
scheduledChange: null,
startedAt: '2023-08-11T08:07:35.449123Z',
status: 'active',
transactionId: 'txn_01h8bxpvx398a7zbawb77y0kp5',
updatedAt: '2023-08-11T08:07:36.892822Z',
},
eventId: 'evt_01h7ht60mmw6d4sf4h38g3t4yq',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,6 @@ export const SubscriptionCanceledMockExpectation = {
scheduledChange: null,
startedAt: '2023-08-11T08:07:35.449123Z',
status: 'canceled',
transactionId: 'txn_01h8bxpvx398a7zbawb77y0kp5',
updatedAt: '2024-01-11T08:34:01.798065409Z',
},
eventId: 'evt_01h7jk37p1ezj1k5b4kt83t35j',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,6 @@ export const SubscriptionImportedMockExpectation = {
scheduledChange: null,
startedAt: '2023-04-13T09:07:04.730931Z',
status: 'active',
transactionId: 'txn_01h8bxpvx398a7zbawb77y0kp5',
updatedAt: '2023-04-13T09:07:06.628577Z',
},
eventId: 'evt_01gxwxwnghn8xa7amfwqb0992q',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,6 @@ export const SubscriptionPastDueMockExpectation = {
scheduledChange: null,
startedAt: '2023-08-11T08:07:35.449123Z',
status: 'past_due',
transactionId: 'txn_01h8bxpvx398a7zbawb77y0kp5',
updatedAt: '2023-10-11T08:08:04.929417Z',
},
eventId: 'evt_01h7jagte1wnq80w5bw5gbmrwk',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,6 @@ export const SubscriptionPausedMockExpectation = {
scheduledChange: null,
startedAt: '2023-08-11T08:07:35.449123Z',
status: 'paused',
transactionId: 'txn_01h8bxpvx398a7zbawb77y0kp5',
updatedAt: '2023-11-11T08:08:19.863737035Z',
},
eventId: 'evt_01h7jcst3syp03dk5f0m8h204f',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,6 @@ export const SubscriptionResumedMockExpectation = {
scheduledChange: null,
startedAt: '2023-08-11T08:07:35.449123Z',
status: 'active',
transactionId: 'txn_01h8bxpvx398a7zbawb77y0kp5',
updatedAt: '2023-11-11T08:33:04.453253Z',
},
eventId: 'evt_01h7je74dkvjc4b2pt8sgsfm7f',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@ export const SubscriptionTrialingMockExpectation = {
startedAt: '2023-08-18T13:15:46.864158Z',
status: 'trialing',
updatedAt: '2023-08-18T13:15:46.864163Z',
transactionId: 'txn_01h8bxpvx398a7zbawb77y0kp5',
},
eventId: 'evt_01h84cka4p40e737vm1ajb2bc5',
eventType: 'subscription.trialing',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,6 @@ export const SubscriptionUpdatedMockExpectation = {
scheduledChange: null,
startedAt: '2023-08-11T08:07:35.449123Z',
status: 'active',
transactionId: 'txn_01h8bxpvx398a7zbawb77y0kp5',
updatedAt: '2023-09-11T08:07:47.341611Z',
},
eventId: 'evt_01h7j296f40h99m4dcrr6h4as8',
Expand Down
14 changes: 13 additions & 1 deletion src/__tests__/mocks/resources/customers.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* Changes may be overwritten as part of auto-generation.
*/

import { ICreditBalanceResponse, ICustomerResponse } from '../../../types/index.js';
import { IAuthTokenResponse, ICreditBalanceResponse, ICustomerResponse } from '../../../types/index.js';
import { Response, ResponsePaginated } from '../../../internal/index.js';

export const UpdateCustomerMock = {
Expand Down Expand Up @@ -36,6 +36,11 @@ export const CustomerMock: ICustomerResponse = {
import_meta: { external_id: '9b95b0b8-e10f-441a-862e-1936a6d818ab', imported_from: 'billing_platform' },
};

export const GenerateAuthTokenMock: IAuthTokenResponse = {
customer_auth_token: 'pca_01hwyzq8hmdwed5p4jc4hnv6bh_01hwwggymjn0yhhb2gr4p91276_6xaav4lydudt6bgmuefeaf2xnu3umegx',
expires_at: '2024-10-13T07:20:50.52Z',
};

export const CustomerCreditBalanceMock: ICreditBalanceResponse = {
balance: {
available: '200',
Expand Down Expand Up @@ -72,3 +77,10 @@ export const ListCustomerMockResponse: ResponsePaginated<ICustomerResponse> = {
},
},
};

export const GenerateAuthTokenMockResponse: Response<IAuthTokenResponse> = {
data: GenerateAuthTokenMock,
meta: {
request_id: '',
},
};
2 changes: 2 additions & 0 deletions src/__tests__/mocks/resources/notification-settings.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const UpdateNotificationSettingsMock: UpdateNotificationSettingsRequestBo
apiVersion: 10,
includeSensitiveFields: true,
subscribedEvents: ['address.updated'],
trafficSource: 'platform',
};

export const CreateNotificationSettingsExpectation = {
Expand All @@ -41,6 +42,7 @@ export const UpdateNotificationSettingsExpectation = {
api_version: 10,
include_sensitive_fields: true,
subscribed_events: ['address.updated'],
traffic_source: 'platform',
};

export const NotificationSettingsMock: INotificationSettingsResponse = {
Expand Down
47 changes: 47 additions & 0 deletions src/__tests__/mocks/resources/payment-methods.mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* ! Autogenerated code !
* Do not make changes to this file.
* Changes may be overwritten as part of auto-generation.
*/

import { IPaymentMethodResponse } from '../../../types/index.js';
import { Response, ResponsePaginated } from '../../../internal/index.js';

export const PaymentMethodMock: IPaymentMethodResponse = {
id: 'paymtd_123',
customer_id: 'ctm_123',
address_id: 'add_123',
type: 'card',
card: {
type: 'visa',
last4: '1234',
expiry_month: 1,
expiry_year: 2025,

cardholder_name: 'Sam Miller',
},
paypal: null,
origin: 'saved_during_purchase',
saved_at: '2024-05-03T11:50:23.422Z',
updated_at: '2024-05-03T11:50:23.422Z',
};

export const PaymentMethodMockResponse: Response<IPaymentMethodResponse> = {
data: PaymentMethodMock,
meta: {
request_id: '',
},
};

export const ListPaymentMethodMockResponse: ResponsePaginated<IPaymentMethodResponse> = {
data: [PaymentMethodMock],
meta: {
request_id: '',
pagination: {
estimated_total: 10,
has_more: true,
next: '/customers/ctm_123/payment-methods?after=1',
per_page: 10,
},
},
};
10 changes: 10 additions & 0 deletions src/__tests__/notifications/notifications-parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ import {
DiscountImportedMockExpectation,
} from '../mocks/notifications/discount-imported.mock.js';
import { DiscountUpdatedMock, DiscountUpdatedMockExpectation } from '../mocks/notifications/discount-updated.mock.js';
import {
PaymentMethodDeletedMock,
PaymentMethodDeletedMockExpectation,
} from '../mocks/notifications/payment-method-deleted.mock';
import {
PaymentMethodSavedMock,
PaymentMethodSavedMockExpectation,
} from '../mocks/notifications/payment-method-saved.mock';
import { PayoutCreatedMock, PayoutCreatedMockExpectation } from '../mocks/notifications/payout-created.mock.js';
import { PayoutPaidMock, PayoutPaidMockExpectation } from '../mocks/notifications/payout-paid.mock.js';
import { PriceCreatedMock, PriceCreatedMockExpectation } from '../mocks/notifications/price-created.mock.js';
Expand Down Expand Up @@ -125,6 +133,8 @@ describe('Notifications Parser', () => {
[DiscountCreatedMock.event_type, DiscountCreatedMock, DiscountCreatedMockExpectation],
[DiscountImportedMock.event_type, DiscountImportedMock, DiscountImportedMockExpectation],
[DiscountUpdatedMock.event_type, DiscountUpdatedMock, DiscountUpdatedMockExpectation],
[PaymentMethodDeletedMock.event_type, PaymentMethodDeletedMock, PaymentMethodDeletedMockExpectation],
[PaymentMethodSavedMock.event_type, PaymentMethodSavedMock, PaymentMethodSavedMockExpectation],
[PayoutCreatedMock.event_type, PayoutCreatedMock, PayoutCreatedMockExpectation],
[PayoutPaidMock.event_type, PayoutPaidMock, PayoutPaidMockExpectation],
[PriceCreatedMock.event_type, PriceCreatedMock, PriceCreatedMockExpectation],
Expand Down
13 changes: 13 additions & 0 deletions src/__tests__/resources/customers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
CustomerCreditBalanceMockResponse,
CustomerMock,
CustomerMockResponse,
GenerateAuthTokenMockResponse,
ListCustomerMockResponse,
UpdateCustomerExpectation,
UpdateCustomerMock,
Expand Down Expand Up @@ -139,4 +140,16 @@ describe('CustomersResource', () => {
});
expect(customer).toBeDefined();
});

test('should generate an auth token for a customer', async () => {
const customerId = CustomerMock.id;
const paddleInstance = getPaddleTestClient();
paddleInstance.post = jest.fn().mockResolvedValue(GenerateAuthTokenMockResponse);

const customersResource = new CustomersResource(paddleInstance);
const authToken = (await customersResource.generateAuthToken(customerId)).customerAuthToken;

expect(paddleInstance.post).toBeCalledWith(`/customers/${customerId}/auth-token`, undefined);
expect(authToken).toBeDefined();
});
});
80 changes: 80 additions & 0 deletions src/__tests__/resources/payment-methods.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* ! Autogenerated code !
* Do not make changes to this file.
* Changes may be overwritten as part of auto-generation.
*/

import { PaymentMethodsResource, type ListCustomerPaymentMethodQueryParameters } from '../../resources/index.js';
import { getPaddleTestClient } from '../helpers/test-client.js';
import {
PaymentMethodMockResponse,
PaymentMethodMock,
ListPaymentMethodMockResponse,
} from '../mocks/resources/payment-methods.mock.js';

describe('PaymentMethodsResource', () => {
test('should return a list of payment methods', async () => {
const customerId = PaymentMethodMock.customer_id;

const paddleInstance = getPaddleTestClient();
paddleInstance.get = jest.fn().mockResolvedValue(ListPaymentMethodMockResponse);

const paymentMethodsResource = new PaymentMethodsResource(paddleInstance);
const paymentMethodCollection = paymentMethodsResource.list(customerId);

let paymentMethods = await paymentMethodCollection.next();
expect(paddleInstance.get).toBeCalledWith(`/customers/${customerId}/payment-methods?`);
expect(paymentMethods.length).toBe(1);

paymentMethods = await paymentMethodCollection.next();
expect(paddleInstance.get).toBeCalledWith(`/customers/${customerId}/payment-methods?after=1`);
expect(paymentMethods.length).toBe(1);
});

test('should accept query params and return a list of payment methods', async () => {
const customerId = PaymentMethodMock.customer_id;

const paddleInstance = getPaddleTestClient();
paddleInstance.get = jest.fn().mockResolvedValue(ListPaymentMethodMockResponse);
const paymentMethodsResource = new PaymentMethodsResource(paddleInstance);
const queryParams: ListCustomerPaymentMethodQueryParameters = {
after: '2',
addressId: ['adr_123'],
};

const paymentMethodCollection = paymentMethodsResource.list(customerId, queryParams);
let paymentMethods = await paymentMethodCollection.next();

expect(paddleInstance.get).toBeCalledWith(`/customers/${customerId}/payment-methods?after=2&address_id=adr_123`);
expect(paymentMethods.length).toBe(1);
});

test('should return a single payment method', async () => {
const paymentMethodId = PaymentMethodMock.id;
const customerId = PaymentMethodMock.customer_id;

const paddleInstance = getPaddleTestClient();
paddleInstance.get = jest.fn().mockResolvedValue(PaymentMethodMockResponse);

const paymentMethodsResource = new PaymentMethodsResource(paddleInstance);
const paymentMethod = await paymentMethodsResource.get(customerId, paymentMethodId);

expect(paddleInstance.get).toBeCalledWith(`/customers/${customerId}/payment-methods/${paymentMethodId}`);
expect(paymentMethod).toBeDefined();
expect(paymentMethod.id).toBe(paymentMethodId);
});

test('should delete an existing payment method', async () => {
const paymentMethodId = PaymentMethodMock.id;
const customerId = PaymentMethodMock.customer_id;

const paddleInstance = getPaddleTestClient();
paddleInstance.delete = jest.fn().mockResolvedValue(PaymentMethodMockResponse);

const paymentMethodsResource = new PaymentMethodsResource(paddleInstance);
const updatedPaymentMethod = await paymentMethodsResource.delete(customerId, paymentMethodId);

expect(paddleInstance.delete).toBeCalledWith(`/customers/${customerId}/payment-methods/${paymentMethodId}`);
expect(updatedPaymentMethod).toBeUndefined();
});
});
Loading
Loading