Skip to content

Commit

Permalink
Merge pull request #4 from sourcefuse/gh-stripe
Browse files Browse the repository at this point in the history
feat(provider): add stripe billing integration
  • Loading branch information
yeshamavani authored Dec 12, 2024
2 parents 79e37b3 + d0720c3 commit dc4b328
Show file tree
Hide file tree
Showing 16 changed files with 14,515 additions and 14,138 deletions.
28,024 changes: 13,966 additions & 14,058 deletions package-lock.json

Large diffs are not rendered by default.

160 changes: 80 additions & 80 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,84 +1,84 @@
{
"name": "loopback4-billing",
"version": "0.0.1",
"description": "loopback4-billing",
"keywords": [
"loopback-extension",
"loopback",
"loopback4-billing"
],
"main": "dist/index.js",
"types": "dist/index.d.ts",
"engines": {
"node": ">=18"
},
"scripts": {
"build": "npm run clean && lb-tsc",
"build:watch": "lb-tsc --watch",
"clean": "lb-clean dist *.tsbuildinfo",
"lint": "npm run prettier:check && npm run eslint",
"lint:fix": "npm run eslint:fix && npm run prettier:fix",
"prettier:cli": "lb-prettier \"**/*.ts\" \"**/*.js\"",
"prettier:check": "npm run prettier:cli -- -l",
"prettier:fix": "npm run prettier:cli -- --write",
"eslint": "lb-eslint --report-unused-disable-directives .",
"eslint:fix": "npm run eslint -- --fix",
"pretest": "npm run build",
"test": "echo No Tests",
"posttest": "npm run lint",
"test:dev": "lb-mocha --allow-console-logs dist/__tests__/**/*.js && npm run posttest",
"prepublishOnly": "npm run test",
"prepare": "husky install"
},
"repository": {
"type": "git",
"url": "https://github.com/sourcefuse/loopback4-billing"
},
"author": "Sourcefuse",
"license": "MIT",
"files": [
"README.md",
"index.js",
"index.d.ts",
"dist",
"src",
"!*/__tests__"

],
"peerDependencies": {
"@loopback/core": "^6.0.0"
},
"dependencies": {
"@loopback/rest": "^14.0.0",
"@loopback/rest-explorer": "^7.0.0",
"chargebee": "^2.38.0",
"tslib": "^2.6.2"
},
"devDependencies": {
"@loopback/build": "^11.0.0",
"@loopback/core": "^6.0.0",
"@loopback/eslint-config": "^14.0.5",
"@loopback/testlab": "^6.1.5",
"@types/node": "^18.11.9",
"source-map-support": "^0.5.21",
"typescript": "~5.2.2",
"husky": "^7.0.4",
"@commitlint/cli": "^17.7.1",
"@commitlint/config-conventional": "^17.7.0",
"@semantic-release/changelog": "^6.0.1",
"@semantic-release/commit-analyzer": "^9.0.2",
"@semantic-release/git": "^10.0.1",
"@semantic-release/npm": "^9.0.1",
"@semantic-release/release-notes-generator": "^10.0.3",
"commitizen": "^4.2.4",
"cz-conventional-changelog": "^3.3.0",
"cz-customizable": "^6.3.0",
"cz-customizable-ghooks": "^2.0.0",
"eslint": "^8.57.0",
"semantic-release": "^19.0.3",
"simple-git": "^3.15.1"
},
"publishConfig": {
"name": "loopback4-billing",
"version": "0.0.1",
"description": "loopback4-billing",
"keywords": [
"loopback-extension",
"loopback",
"loopback4-billing"
],
"main": "dist/index.js",
"types": "dist/index.d.ts",
"engines": {
"node": ">=18"
},
"scripts": {
"build": "npm run clean && lb-tsc",
"build:watch": "lb-tsc --watch",
"clean": "lb-clean dist *.tsbuildinfo",
"lint": "npm run prettier:check && npm run eslint",
"lint:fix": "npm run eslint:fix && npm run prettier:fix",
"prettier:cli": "lb-prettier \"**/*.ts\" \"**/*.js\"",
"prettier:check": "npm run prettier:cli -- -l",
"prettier:fix": "npm run prettier:cli -- --write",
"eslint": "lb-eslint --report-unused-disable-directives .",
"eslint:fix": "npm run eslint -- --fix",
"pretest": "npm run build",
"test": "echo No Tests",
"posttest": "npm run lint",
"test:dev": "lb-mocha --allow-console-logs dist/__tests__/**/*.js && npm run posttest",
"prepublishOnly": "npm run test",
"prepare": "husky install"
},
"repository": {
"type": "git",
"url": "https://github.com/sourcefuse/loopback4-billing"
},
"author": "Sourcefuse",
"license": "MIT",
"files": [
"README.md",
"index.js",
"index.d.ts",
"dist",
"src",
"!*/__tests__"
],
"peerDependencies": {
"@loopback/core": "^6.0.0"
},
"dependencies": {
"@loopback/rest": "^14.0.0",
"@loopback/rest-explorer": "^7.0.0",
"chargebee": "^2.38.0",
"stripe": "~17.2.0",
"tslib": "^2.6.2"
},
"devDependencies": {
"@loopback/build": "^11.0.0",
"@loopback/core": "^6.0.0",
"@loopback/eslint-config": "^14.0.5",
"@loopback/testlab": "^6.1.5",
"@types/node": "^18.11.9",
"source-map-support": "^0.5.21",
"typescript": "~5.2.2",
"husky": "^7.0.4",
"@commitlint/cli": "^17.7.1",
"@commitlint/config-conventional": "^17.7.0",
"@semantic-release/changelog": "^6.0.1",
"@semantic-release/commit-analyzer": "^9.0.2",
"@semantic-release/git": "^10.0.1",
"@semantic-release/npm": "^9.0.1",
"@semantic-release/release-notes-generator": "^10.0.3",
"commitizen": "^4.2.4",
"cz-conventional-changelog": "^3.3.0",
"cz-customizable": "^6.3.0",
"cz-customizable-ghooks": "^2.0.0",
"eslint": "^8.57.0",
"semantic-release": "^19.0.3",
"simple-git": "^3.15.1"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/"
},
"config": {
Expand Down
1 change: 1 addition & 0 deletions src/providers/sdk/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './chargebee';
export * from './stripe';
64 changes: 64 additions & 0 deletions src/providers/sdk/stripe/adapter/customer.adapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import {AnyObject} from '@loopback/repository';
import {IAdapter} from '../../../../types';
import {IStripeCustomer} from '../type';
export class StripeCustomerAdapter implements IAdapter<IStripeCustomer> {
constructor() {}

adaptToModel(resp: AnyObject): IStripeCustomer {
const {firstName, lastName} = splitName(resp.name);
const res: IStripeCustomer = {
id: resp.id,
firstName: firstName,
lastName: lastName,
email: resp.email,
phone: resp.phone,
billingAddress: {
firstName: firstName,
lastName: lastName,
email: resp.email,
phone: resp.phone,
line1: resp.address?.line1,
line2: resp.address?.line2,
city: resp.address?.city,
state: resp.address?.state,
zip: resp.address?.postal_code,
country: resp.address?.country,
},
};
return res;
}
adaptFromModel(data: IStripeCustomer): AnyObject {
return {
name: data.firstName + ' ' + data.lastName,
email: data.email,
phone: data.phone,
address: {
line1: data.billingAddress?.line1,
line2: data.billingAddress?.line2,
city: data.billingAddress?.city,
state: data.billingAddress?.state,
country: data.billingAddress?.country,
/* eslint-disable-next-line @typescript-eslint/naming-convention */
postal_code: data.billingAddress?.zip,
},
};
}
}

function splitName(fullName: string): {firstName: string; lastName: string} {
const nameParts = fullName.trim().split(' ');

if (nameParts.length === 1) {
// If only first name is provided
return {
firstName: nameParts[0],
lastName: '',
};
} else {
// If both first and last names are provided
return {
firstName: nameParts[0],
lastName: nameParts.slice(1).join(' '), // Handles multiple middle/last names
};
}
}
3 changes: 3 additions & 0 deletions src/providers/sdk/stripe/adapter/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './customer.adapter';
export * from './invoice.adapter';
export * from './payment-source.adapter';
72 changes: 72 additions & 0 deletions src/providers/sdk/stripe/adapter/invoice.adapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/* eslint-disable @typescript-eslint/naming-convention */
import {AnyObject} from '@loopback/repository';
import {IAdapter} from '../../../../types';
import {IStripeInvoice} from '../type';
export class StripeInvoiceAdapter implements IAdapter<IStripeInvoice> {
constructor() {}

adaptToModel(resp: AnyObject): IStripeInvoice {
return {
id: resp.id,
customerId: resp.customer,
status: resp.status,
currencyCode: resp.currency,
shippingAddress: resp.shipping_details?.address
? {
firstName: resp.customer_name?.split(' ')[0] || '',
lastName: resp.customer_name?.split(' ')[1] || '',
line1: resp.shipping_details.address.line1,
line2: resp.shipping_details.address.line2,
city: resp.shipping_details.address.city,
state: resp.shipping_details.address.state,
zip: resp.shipping_details.address.postal_code,
country: resp.shipping_details.address.country,
phone: resp.shipping_details.phone,
email: resp.customer_email,
}
: undefined,
charges: resp.lines?.data.map(
(
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
lineItem: any, // NOSONAR
) => ({
amount: lineItem.amount / 100, // divided by 100 because the lineItem.amount is coming in cents
description: lineItem.description,
}),
),
options: {
autoAdvnace: resp.auto_advance || false,
},
};
}
adaptFromModel(data: IStripeInvoice): AnyObject {
const shippingDetails: AnyObject = data.shippingAddress
? {
shipping_details: {
name: [
data.shippingAddress.firstName,
data.shippingAddress.lastName,
]
.join(' ')
.trim(),
address: {
line1: data.shippingAddress.line1,
line2: data.shippingAddress.line2,
city: data.shippingAddress.city,
state: data.shippingAddress.state,
postal_code: data.shippingAddress.zip,
country: data.shippingAddress.country,
},
phone: data.shippingAddress.phone,
},
}
: {};

return {
customer: data.customerId,
currency: data.currencyCode,
...shippingDetails,
auto_advance: data.options?.autoAdvnace ?? false,
};
}
}
28 changes: 28 additions & 0 deletions src/providers/sdk/stripe/adapter/payment-source.adapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {AnyObject} from '@loopback/repository';
import {IAdapter} from '../../../../types';
import {IStripePaymentSource} from '../type';
export class StripePaymentAdapter implements IAdapter<IStripePaymentSource> {
constructor() {}

adaptToModel(resp: AnyObject): IStripePaymentSource {
return {
id: resp.id,
customerId: resp.customer,
card: resp.card
? {
gatewayAccountId: resp.card.fingerprint, // Using fingerprint as an identifier
number: '**** **** **** ' + resp.card.last4, // Masked card number
expiryMonth: resp.card.exp_month,
expiryYear: resp.card.exp_year,
cvv: '***', // CVV is not returned by Stripe, so it should be masked
}
: undefined,
options: {
token: resp.id, // Use the payment method id as the token if needed
},
};
}
adaptFromModel(data: IStripePaymentSource): AnyObject {
return {}; // This is intentional
}
}
5 changes: 5 additions & 0 deletions src/providers/sdk/stripe/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from './adapter';
export * from './key';
export * from './stripe.provider';
export * from './stripe.service';
export * from './type';
12 changes: 12 additions & 0 deletions src/providers/sdk/stripe/key.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) 2023 Sourcefuse Technologies
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
import {BindingKey} from '@loopback/core';
import {StripeConfig} from './type';

export namespace StripeBindings {
export const config = BindingKey.create<StripeConfig>(
'sf.provider.stripe.config',
);
}
15 changes: 15 additions & 0 deletions src/providers/sdk/stripe/stripe.provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {Provider, inject} from '@loopback/core';
import {StripeBindings} from './key';
import {StripeService} from './stripe.service';
import {IStripeService, StripeConfig} from './type';

export class StripeServiceProvider implements Provider<IStripeService> {
constructor(
@inject(StripeBindings.config, {optional: true})
private readonly stripeConfig: StripeConfig,
) {}

value(): IStripeService {
return new StripeService(this.stripeConfig);
}
}
Loading

0 comments on commit dc4b328

Please sign in to comment.