зеркало из https://github.com/mozilla/fxa.git
Merge pull request #16607 from mozilla/FXA-8939
feat(payments-paypal): Create PaypalManager getCustomerBillingAgreementId
This commit is contained in:
Коммит
500b2a0b09
|
@ -21,6 +21,11 @@ import {
|
||||||
import { PayPalClient } from './paypal.client';
|
import { PayPalClient } from './paypal.client';
|
||||||
import { PayPalManager } from './paypal.manager';
|
import { PayPalManager } from './paypal.manager';
|
||||||
import { BillingAgreementStatus } from './paypal.types';
|
import { BillingAgreementStatus } from './paypal.types';
|
||||||
|
import {
|
||||||
|
PaypalCustomerNotFoundError,
|
||||||
|
PaypalCustomerMultipleRecordsError,
|
||||||
|
} from './paypalCustomer/paypalCustomer.error';
|
||||||
|
import { ResultPaypalCustomerFactory } from './paypalCustomer/paypalCustomer.factories';
|
||||||
import { PaypalCustomerManager } from './paypalCustomer/paypalCustomer.manager';
|
import { PaypalCustomerManager } from './paypalCustomer/paypalCustomer.manager';
|
||||||
|
|
||||||
describe('PaypalManager', () => {
|
describe('PaypalManager', () => {
|
||||||
|
@ -147,6 +152,48 @@ describe('PaypalManager', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getCustomerBillingAgreementId', () => {
|
||||||
|
it("returns the customer's current PayPal billing agreement ID", async () => {
|
||||||
|
const mockPayPalCustomer = ResultPaypalCustomerFactory();
|
||||||
|
const mockStripeCustomer = CustomerFactory();
|
||||||
|
|
||||||
|
paypalCustomerManager.fetchPaypalCustomersByUid = jest
|
||||||
|
.fn()
|
||||||
|
.mockResolvedValueOnce([mockPayPalCustomer]);
|
||||||
|
|
||||||
|
const result = await paypalManager.getCustomerBillingAgreementId(
|
||||||
|
mockStripeCustomer
|
||||||
|
);
|
||||||
|
expect(result).toEqual(mockPayPalCustomer.billingAgreementId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws PaypalCustomerNotFoundError if no PayPal customer record', async () => {
|
||||||
|
const mockStripeCustomer = CustomerFactory();
|
||||||
|
|
||||||
|
paypalCustomerManager.fetchPaypalCustomersByUid = jest
|
||||||
|
.fn()
|
||||||
|
.mockResolvedValueOnce([]);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
paypalManager.getCustomerBillingAgreementId(mockStripeCustomer)
|
||||||
|
).rejects.toBeInstanceOf(PaypalCustomerNotFoundError);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws PaypalCustomerMultipleRecordsError if more than one PayPal customer found', async () => {
|
||||||
|
const mockPayPalCustomer1 = ResultPaypalCustomerFactory();
|
||||||
|
const mockPayPalCustomer2 = ResultPaypalCustomerFactory();
|
||||||
|
const mockStripeCustomer = CustomerFactory();
|
||||||
|
|
||||||
|
paypalCustomerManager.fetchPaypalCustomersByUid = jest
|
||||||
|
.fn()
|
||||||
|
.mockResolvedValueOnce([mockPayPalCustomer1, mockPayPalCustomer2]);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
paypalManager.getCustomerBillingAgreementId(mockStripeCustomer)
|
||||||
|
).rejects.toBeInstanceOf(PaypalCustomerMultipleRecordsError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('getCustomerPayPalSubscriptions', () => {
|
describe('getCustomerPayPalSubscriptions', () => {
|
||||||
it('return customer subscriptions where collection method is send_invoice', async () => {
|
it('return customer subscriptions where collection method is send_invoice', async () => {
|
||||||
const mockPayPalSubscription = SubscriptionFactory({
|
const mockPayPalSubscription = SubscriptionFactory({
|
||||||
|
|
|
@ -12,6 +12,10 @@ import {
|
||||||
import { AccountDatabase } from '@fxa/shared/db/mysql/account';
|
import { AccountDatabase } from '@fxa/shared/db/mysql/account';
|
||||||
import { PayPalClient } from './paypal.client';
|
import { PayPalClient } from './paypal.client';
|
||||||
import { BillingAgreement, BillingAgreementStatus } from './paypal.types';
|
import { BillingAgreement, BillingAgreementStatus } from './paypal.types';
|
||||||
|
import {
|
||||||
|
PaypalCustomerNotFoundError,
|
||||||
|
PaypalCustomerMultipleRecordsError,
|
||||||
|
} from './paypalCustomer/paypalCustomer.error';
|
||||||
import { PaypalCustomerManager } from './paypalCustomer/paypalCustomer.manager';
|
import { PaypalCustomerManager } from './paypalCustomer/paypalCustomer.manager';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -54,11 +58,27 @@ export class PayPalManager {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the customer’s current paypal billing agreement ID from the
|
||||||
|
* auth database via the Paypal repository
|
||||||
|
*/
|
||||||
|
async getCustomerBillingAgreementId(customer: Stripe.Customer) {
|
||||||
|
const paypalCustomer =
|
||||||
|
await this.paypalCustomerManager.fetchPaypalCustomersByUid(
|
||||||
|
customer.metadata.userid
|
||||||
|
);
|
||||||
|
const firstRecord = paypalCustomer.at(0);
|
||||||
|
|
||||||
|
if (!firstRecord)
|
||||||
|
throw new PaypalCustomerNotFoundError(customer.metadata.uid);
|
||||||
|
if (paypalCustomer.length > 1)
|
||||||
|
throw new PaypalCustomerMultipleRecordsError(customer.metadata.uid);
|
||||||
|
|
||||||
|
return firstRecord.billingAgreementId;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves PayPal subscriptions
|
* Retrieves PayPal subscriptions
|
||||||
*
|
|
||||||
* @param customer
|
|
||||||
* @returns
|
|
||||||
*/
|
*/
|
||||||
async getCustomerPayPalSubscriptions(customer: Stripe.Customer) {
|
async getCustomerPayPalSubscriptions(customer: Stripe.Customer) {
|
||||||
const subscriptions = await this.stripeManager.getSubscriptions(
|
const subscriptions = await this.stripeManager.getSubscriptions(
|
||||||
|
@ -82,10 +102,6 @@ export class PayPalManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process an invoice when amount is greater than minimum amount
|
* Process an invoice when amount is greater than minimum amount
|
||||||
*
|
|
||||||
* @param customer
|
|
||||||
* @param invoice
|
|
||||||
* @param ipaddress
|
|
||||||
*/
|
*/
|
||||||
async processNonZeroInvoice(
|
async processNonZeroInvoice(
|
||||||
customer: Stripe.Customer,
|
customer: Stripe.Customer,
|
||||||
|
@ -99,8 +115,6 @@ export class PayPalManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finalize and process a draft invoice that has no amounted owed.
|
* Finalize and process a draft invoice that has no amounted owed.
|
||||||
*
|
|
||||||
* @param invoice
|
|
||||||
*/
|
*/
|
||||||
async processZeroInvoice(invoiceId: string) {
|
async processZeroInvoice(invoiceId: string) {
|
||||||
// It appears for subscriptions that do not require payment, the invoice
|
// It appears for subscriptions that do not require payment, the invoice
|
||||||
|
@ -113,8 +127,6 @@ export class PayPalManager {
|
||||||
* Process an invoice
|
* Process an invoice
|
||||||
* If amount is less than minimum amount, call processZeroInvoice
|
* If amount is less than minimum amount, call processZeroInvoice
|
||||||
* If amount is greater than minimum amount, call processNonZeroInvoice (legacy PaypalHelper processInvoice)
|
* If amount is greater than minimum amount, call processNonZeroInvoice (legacy PaypalHelper processInvoice)
|
||||||
*
|
|
||||||
* @param invoice
|
|
||||||
*/
|
*/
|
||||||
async processInvoice(invoice: Stripe.Invoice) {
|
async processInvoice(invoice: Stripe.Invoice) {
|
||||||
const amountInCents = invoice.amount_due;
|
const amountInCents = invoice.amount_due;
|
||||||
|
|
|
@ -23,7 +23,7 @@ export class PaypalCustomerNotCreatedError extends PaypalCustomerManagerError {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PaypalCustomerNotFoundError extends PaypalCustomerManagerError {
|
export class PaypalCustomerNotFoundError extends PaypalCustomerManagerError {
|
||||||
constructor(uid: string, cause: Error) {
|
constructor(uid: string, cause?: Error) {
|
||||||
super('PaypalCustomer not found', {
|
super('PaypalCustomer not found', {
|
||||||
info: {
|
info: {
|
||||||
uid,
|
uid,
|
||||||
|
@ -60,3 +60,14 @@ export class PaypalCustomerNotDeletedError extends PaypalCustomerManagerError {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class PaypalCustomerMultipleRecordsError extends PaypalCustomerManagerError {
|
||||||
|
constructor(uid: string, cause?: Error) {
|
||||||
|
super('Multiple PaypalCustomer records', {
|
||||||
|
info: {
|
||||||
|
uid,
|
||||||
|
},
|
||||||
|
cause,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -20,9 +20,6 @@ export class StripeClient {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a customer record directly from Stripe
|
* Retrieves a customer record directly from Stripe
|
||||||
*
|
|
||||||
* @param customerId The Stripe customer ID of the customer to fetch
|
|
||||||
* @returns The customer record for the customerId provided
|
|
||||||
*/
|
*/
|
||||||
async fetchCustomer(customerId: string) {
|
async fetchCustomer(customerId: string) {
|
||||||
return this.stripe.customers.retrieve(customerId);
|
return this.stripe.customers.retrieve(customerId);
|
||||||
|
@ -44,9 +41,6 @@ export class StripeClient {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves subscriptions directly from Stripe
|
* Retrieves subscriptions directly from Stripe
|
||||||
*
|
|
||||||
* @param customerId
|
|
||||||
* @returns
|
|
||||||
*/
|
*/
|
||||||
async fetchSubscriptions(customerId: string) {
|
async fetchSubscriptions(customerId: string) {
|
||||||
return this.stripe.subscriptions.list({
|
return this.stripe.subscriptions.list({
|
||||||
|
|
|
@ -22,9 +22,6 @@ export class StripeManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a customer record
|
* Retrieves a customer record
|
||||||
*
|
|
||||||
* @param customerId
|
|
||||||
* @returns
|
|
||||||
*/
|
*/
|
||||||
async fetchActiveCustomer(customerId: string) {
|
async fetchActiveCustomer(customerId: string) {
|
||||||
const customer = await this.client.fetchCustomer(customerId);
|
const customer = await this.client.fetchCustomer(customerId);
|
||||||
|
@ -44,9 +41,6 @@ export class StripeManager {
|
||||||
/**
|
/**
|
||||||
* Returns minimum amount for valid currency
|
* Returns minimum amount for valid currency
|
||||||
* Throws error for invalid currency
|
* Throws error for invalid currency
|
||||||
*
|
|
||||||
* @param currency
|
|
||||||
* @returns
|
|
||||||
*/
|
*/
|
||||||
getMinimumAmount(currency: string): number {
|
getMinimumAmount(currency: string): number {
|
||||||
if (STRIPE_MINIMUM_CHARGE_AMOUNTS[currency]) {
|
if (STRIPE_MINIMUM_CHARGE_AMOUNTS[currency]) {
|
||||||
|
@ -58,8 +52,6 @@ export class StripeManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves subscriptions
|
* Retrieves subscriptions
|
||||||
*
|
|
||||||
* @param customerId
|
|
||||||
*/
|
*/
|
||||||
async getSubscriptions(customerId: string) {
|
async getSubscriptions(customerId: string) {
|
||||||
return this.client.fetchSubscriptions(customerId);
|
return this.client.fetchSubscriptions(customerId);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче