Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
6f0952bc22 | |||
08c78bae05 | |||
406178d6d0 | |||
d6a0a9d438 |
46
package-lock.json
generated
46
package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mojoio/paypal",
|
||||
"version": "1.0.9",
|
||||
"version": "1.0.11",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@ -10492,6 +10492,50 @@
|
||||
"resolved": "https://verdaccio.lossless.one/tslib/-/tslib-1.13.0.tgz",
|
||||
"integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q=="
|
||||
},
|
||||
"tslint": {
|
||||
"version": "6.1.3",
|
||||
"resolved": "https://verdaccio.lossless.one/tslint/-/tslint-6.1.3.tgz",
|
||||
"integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
"builtin-modules": "^1.1.1",
|
||||
"chalk": "^2.3.0",
|
||||
"commander": "^2.12.1",
|
||||
"diff": "^4.0.1",
|
||||
"glob": "^7.1.1",
|
||||
"js-yaml": "^3.13.1",
|
||||
"minimatch": "^3.0.4",
|
||||
"mkdirp": "^0.5.3",
|
||||
"resolve": "^1.3.2",
|
||||
"semver": "^5.3.0",
|
||||
"tslib": "^1.13.0",
|
||||
"tsutils": "^2.29.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"builtin-modules": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://verdaccio.lossless.one/builtin-modules/-/builtin-modules-1.1.1.tgz",
|
||||
"integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"tslint-config-prettier": {
|
||||
"version": "1.18.0",
|
||||
"resolved": "https://verdaccio.lossless.one/tslint-config-prettier/-/tslint-config-prettier-1.18.0.tgz",
|
||||
"integrity": "sha512-xPw9PgNPLG3iKRxmK7DWr+Ea/SzrvfHtjFt5LBl61gk2UBG/DB9kCXRjv+xyIU1rUtnayLeMUVJBcMX8Z17nDg==",
|
||||
"dev": true
|
||||
},
|
||||
"tsutils": {
|
||||
"version": "2.29.0",
|
||||
"resolved": "https://verdaccio.lossless.one/tsutils/-/tsutils-2.29.0.tgz",
|
||||
"integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "^1.8.1"
|
||||
}
|
||||
},
|
||||
"tty-browserify": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://verdaccio.lossless.one/tty-browserify/-/tty-browserify-0.0.0.tgz",
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@mojoio/paypal",
|
||||
"private": false,
|
||||
"version": "1.0.9",
|
||||
"version": "1.0.11",
|
||||
"description": "mojoio PayPal API abstraction",
|
||||
"main": "dist_ts/index.js",
|
||||
"typings": "dist_ts/index.d.ts",
|
||||
@ -15,7 +15,9 @@
|
||||
"@gitzone/tsbuild": "^2.1.25",
|
||||
"@gitzone/tstest": "^1.0.44",
|
||||
"@pushrocks/qenv": "^4.0.10",
|
||||
"@pushrocks/tapbundle": "^3.2.9"
|
||||
"@pushrocks/tapbundle": "^3.2.9",
|
||||
"tslint": "^6.1.3",
|
||||
"tslint-config-prettier": "^1.18.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@pushrocks/smartrequest": "^1.1.47",
|
||||
|
@ -27,7 +27,6 @@ Platform support | [ or [contribute monthly](https://lossless.link/contribute). :)
|
||||
|
@ -11,12 +11,16 @@ tap.test('should create a valid paypal instance', async () => {
|
||||
testPayPalInstance = new paypal.PayPalAccount({
|
||||
clientId: testQenv.getEnvVarOnDemand('PAYPAL_CLIENT_ID'),
|
||||
clientSecret: testQenv.getEnvVarOnDemand('PAYPAL_CLIENT_SECRET'),
|
||||
accountOwner: 'sample corp'
|
||||
});
|
||||
expect(testPayPalInstance).to.be.instanceOf(paypal.PayPalAccount);
|
||||
});
|
||||
|
||||
tap.test('should get an access token', async () => {
|
||||
const transactions = await paypal.PayPalTransaction.getTransactionFor30days(testPayPalInstance, smarttime.ExtendedDate.fromHyphedDate('2020-05-01').getTime());
|
||||
const transactions = await testPayPalInstance.getTransactionsFromTo(
|
||||
smarttime.ExtendedDate.fromHyphedDate('2020-01-01').getTime(),
|
||||
smarttime.ExtendedDate.fromHyphedDate('2020-08-01').getTime()
|
||||
);
|
||||
console.log(transactions);
|
||||
});
|
||||
|
||||
|
@ -1,2 +1,2 @@
|
||||
export * from './paypal.classes.paypal';
|
||||
export * from './paypal.classes.account';
|
||||
export * from './paypal.classes.transaction';
|
||||
|
153
ts/paypal.classes.account.ts
Normal file
153
ts/paypal.classes.account.ts
Normal file
@ -0,0 +1,153 @@
|
||||
import * as plugins from './paypal.plugins';
|
||||
import { PayPalTransaction } from './paypal.classes.transaction';
|
||||
|
||||
export interface IPayPalOptions {
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
accountOwner: string;
|
||||
}
|
||||
|
||||
export class PayPalAccount {
|
||||
public apiBaseUrl: string = 'https://api.paypal.com';
|
||||
public options: IPayPalOptions;
|
||||
|
||||
private apiToken: string;
|
||||
private apiTokenExpirationTime: number;
|
||||
|
||||
constructor(optionsArg: IPayPalOptions) {
|
||||
this.options = optionsArg;
|
||||
}
|
||||
|
||||
public async getTransactionsFromTo(fromTimeMillisArg: number, toTimeMillisArg: number) {
|
||||
let allTransactions: PayPalTransaction[] = [];
|
||||
// lets note the time from one month before. We need that for accurate transactions pools.
|
||||
const monthBeforeStartMillis =
|
||||
fromTimeMillisArg - plugins.smarttime.getMilliSecondsFromUnits({ days: 30 });
|
||||
do {
|
||||
const transactions = await PayPalTransaction.getTransactionFor30days(this, fromTimeMillisArg);
|
||||
allTransactions = allTransactions.concat(transactions);
|
||||
fromTimeMillisArg =
|
||||
fromTimeMillisArg + plugins.smarttime.getMilliSecondsFromUnits({ days: 30 });
|
||||
} while (fromTimeMillisArg < toTimeMillisArg);
|
||||
|
||||
const invoiceIds: string[] = [];
|
||||
const transactionPools: PayPalTransaction[][] = [];
|
||||
// lets get all invoiceids
|
||||
allTransactions.forEach((transactionArg) => {
|
||||
const invoiceId = transactionArg.data.originApiObject.transaction_info.invoice_id;
|
||||
if (!invoiceIds.includes(invoiceId)) {
|
||||
invoiceIds.push(invoiceId);
|
||||
}
|
||||
});
|
||||
|
||||
// lets get all transactions per invoiceId
|
||||
invoiceIds.forEach((invoiceIdArg) => {
|
||||
const transactionPool: PayPalTransaction[] = [];
|
||||
allTransactions.forEach((transactionArg) => {
|
||||
if (transactionArg.data.originApiObject.transaction_info.invoice_id === invoiceIdArg) {
|
||||
transactionPool.push(transactionArg);
|
||||
}
|
||||
});
|
||||
transactionPools.push(transactionPool);
|
||||
});
|
||||
|
||||
const previousMonthTransactions = await PayPalTransaction.getTransactionFor30days(
|
||||
this,
|
||||
monthBeforeStartMillis
|
||||
);
|
||||
|
||||
transactionPools.forEach((transactionPoolArg) => {
|
||||
const poolInvoiceId = transactionPoolArg[0].data.originApiObject.transaction_info.invoice_id;
|
||||
previousMonthTransactions.forEach((transactionArg) => {
|
||||
if (transactionArg.data.originApiObject.transaction_info.invoice_id === poolInvoiceId) {
|
||||
transactionPoolArg.push(transactionArg);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let finalTransactions: PayPalTransaction[] = [];
|
||||
// lets process all transactionPool
|
||||
transactionPools.forEach((transactionPoolArg) => {
|
||||
// lets detect foreign transactions
|
||||
let hasForeignTransactions = false;
|
||||
transactionPoolArg.forEach((transactionArg) => {
|
||||
if (transactionArg.data.currency !== 'EUR') {
|
||||
hasForeignTransactions = true;
|
||||
}
|
||||
});
|
||||
if (hasForeignTransactions && transactionPoolArg.length < 4) {
|
||||
console.log(
|
||||
`Pool with invoiceId ${transactionPoolArg[0].data.originApiObject.transaction_info.invoice_id} is not completed yet. Omminiting ${transactionPoolArg.length} transactions as a result.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (hasForeignTransactions && transactionPoolArg.length === 4) {
|
||||
const negativeNativeTransaction = transactionPoolArg.find(transactionArg => {
|
||||
return transactionArg.data.amount < 0 && transactionArg.data.currency === 'EUR';
|
||||
});
|
||||
|
||||
const negativeForeignTransaction = transactionPoolArg.find(transactionArg => {
|
||||
return transactionArg.data.amount < 0 && transactionArg.data.currency !== 'EUR';
|
||||
});
|
||||
|
||||
const positiveNativeTransaction = transactionPoolArg.find(transactionArg => {
|
||||
return transactionArg.data.amount > 0 && transactionArg.data.currency === 'EUR';
|
||||
});
|
||||
|
||||
const positiveForeignTransaction = transactionPoolArg.find(transactionArg => {
|
||||
return transactionArg.data.amount > 0 && transactionArg.data.currency !== 'EUR';
|
||||
});
|
||||
|
||||
negativeNativeTransaction.data.name = negativeForeignTransaction.data.name;
|
||||
negativeNativeTransaction.data.description = negativeForeignTransaction.data.description;
|
||||
positiveNativeTransaction.data.name = this.options.accountOwner;
|
||||
positiveNativeTransaction.data.description = 'account balance transfer';
|
||||
transactionPoolArg = transactionPoolArg.filter(transactionArg => transactionArg.data.currency === 'EUR');
|
||||
}
|
||||
if (!hasForeignTransactions && transactionPoolArg.length === 2) {
|
||||
const positiveNativeTransaction = transactionPoolArg.find(transactionArg => {
|
||||
return transactionArg.data.amount > 0 && transactionArg.data.currency === 'EUR';
|
||||
});
|
||||
positiveNativeTransaction.data.name = this.options.accountOwner;
|
||||
positiveNativeTransaction.data.description = 'account balance transfer';
|
||||
}
|
||||
// pool is ready
|
||||
finalTransactions = finalTransactions.concat(transactionPoolArg);
|
||||
});
|
||||
|
||||
return finalTransactions;
|
||||
}
|
||||
|
||||
public async request(methodArg: 'GET' | 'POST', routeArg: string, payloadArg: any) {
|
||||
if (!this.apiToken || this.apiTokenExpirationTime < Date.now()) {
|
||||
const authHeader = `Basic ${plugins.smartstring.base64.encode(
|
||||
`${this.options.clientId}:${this.options.clientSecret}`
|
||||
)}`;
|
||||
const response = await plugins.smartrequest.request(
|
||||
`${this.apiBaseUrl}/v1/oauth2/token?grant_type=client_credentials`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Accept-Language': 'en_US',
|
||||
Authorization: authHeader,
|
||||
},
|
||||
keepAlive: false,
|
||||
}
|
||||
);
|
||||
this.apiToken = response.body.access_token;
|
||||
this.apiTokenExpirationTime = Date.now() + response.body.expires_in * 1000 - 600000;
|
||||
}
|
||||
|
||||
// we have a token
|
||||
const response = await plugins.smartrequest.request(`${this.apiBaseUrl}${routeArg}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Accept-Language': 'en_US',
|
||||
Authorization: `Bearer ${this.apiToken}`,
|
||||
},
|
||||
});
|
||||
return response.body;
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
import * as plugins from './paypal.plugins';
|
||||
|
||||
export interface IPayPalOptions {
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
}
|
||||
|
||||
export class PayPalAccount {
|
||||
public apiBaseUrl: string = 'https://api.paypal.com'
|
||||
public options: IPayPalOptions;
|
||||
|
||||
private apiToken: string;
|
||||
private apiTokenExpirationTime: number;
|
||||
|
||||
constructor(optionsArg: IPayPalOptions) {
|
||||
this.options = optionsArg;
|
||||
}
|
||||
|
||||
public async request(methodArg: 'GET' | 'POST', routeArg: string, payloadArg: any) {
|
||||
if (!this.apiToken || this.apiTokenExpirationTime < Date.now()) {
|
||||
const authHeader = `Basic ${plugins.smartstring.base64.encode(
|
||||
`${this.options.clientId}:${this.options.clientSecret}`
|
||||
)}`;
|
||||
const response = await plugins.smartrequest.request(
|
||||
`${this.apiBaseUrl}/v1/oauth2/token?grant_type=client_credentials`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Accept-Language': 'en_US',
|
||||
Authorization: authHeader,
|
||||
},
|
||||
keepAlive: false,
|
||||
}
|
||||
);
|
||||
this.apiToken = response.body.access_token;
|
||||
this.apiTokenExpirationTime = Date.now() + response.body.expires_in * 1000 - 600000;
|
||||
}
|
||||
|
||||
// we have a token
|
||||
const response = await plugins.smartrequest.request(`${this.apiBaseUrl}${routeArg}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Accept-Language': 'en_US',
|
||||
Authorization: `Bearer ${this.apiToken}`,
|
||||
},
|
||||
});
|
||||
return response.body;
|
||||
}
|
||||
}
|
@ -1,25 +1,59 @@
|
||||
import * as plugins from './paypal.plugins';
|
||||
import { PayPalAccount } from './paypal.classes.paypal';
|
||||
import { PayPalAccount } from './paypal.classes.account';
|
||||
|
||||
export interface IPayPalOriginTransactionApiObject {
|
||||
paypal_account_id: string;
|
||||
transaction_id: string;
|
||||
transaction_event_code: string;
|
||||
transaction_initiation_date: string;
|
||||
transaction_updated_date: string;
|
||||
transaction_amount: { currency_code: string; value: string };
|
||||
transaction_status: string;
|
||||
transaction_subject: string;
|
||||
ending_balance: { currency_code: string; value: string };
|
||||
available_balance: { currency_code: string; value: string };
|
||||
invoice_id: string;
|
||||
protection_eligibility: string;
|
||||
transaction_info: {
|
||||
paypal_account_id: string;
|
||||
transaction_id: string;
|
||||
transaction_event_code: string;
|
||||
transaction_initiation_date: string;
|
||||
transaction_updated_date: string;
|
||||
transaction_amount: { currency_code: string; value: string };
|
||||
transaction_status: string;
|
||||
transaction_subject: string;
|
||||
ending_balance: { currency_code: string; value: string };
|
||||
available_balance: { currency_code: string; value: string };
|
||||
invoice_id: string;
|
||||
protection_eligibility: string;
|
||||
};
|
||||
cart_info: {
|
||||
item_details: {
|
||||
item_code: string;
|
||||
item_name: string;
|
||||
item_quantity: string;
|
||||
item_unit_price: {
|
||||
currency_code: string;
|
||||
value: string;
|
||||
};
|
||||
item_amount: {
|
||||
currency_code: string;
|
||||
value: string;
|
||||
};
|
||||
total_item_amount: {
|
||||
currency_code: string;
|
||||
value: string;
|
||||
};
|
||||
invoice_number: string;
|
||||
}[];
|
||||
};
|
||||
payer_info: {
|
||||
account_id: string;
|
||||
email_address: string;
|
||||
address_status: string;
|
||||
payer_status: string;
|
||||
payer_name: {
|
||||
alternate_full_name: string;
|
||||
};
|
||||
country_code: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface IPayPalTransactionOptions {
|
||||
id: string;
|
||||
originApiObject: IPayPalOriginTransactionApiObject
|
||||
originApiObject: IPayPalOriginTransactionApiObject;
|
||||
amount: number;
|
||||
name: string;
|
||||
description: string;
|
||||
currency: 'USD' | 'EUR';
|
||||
timestampIso: string;
|
||||
}
|
||||
@ -33,23 +67,34 @@ export class PayPalTransaction {
|
||||
const endDate = startDate + plugins.smarttime.units.days(30);
|
||||
const startDateIso = plugins.smarttime.ExtendedDate.fromMillis(startDate).toISOString();
|
||||
const endDateIso = plugins.smarttime.ExtendedDate.fromMillis(endDate).toISOString();
|
||||
console.log(endDateIso);
|
||||
console.log(startDateIso);
|
||||
console.log(`getting PayPal transactions from ${startDateIso} + ${endDateIso}`);
|
||||
const response = await paypalInstanceArg.request(
|
||||
'GET',
|
||||
`/v1/reporting/transactions?start_date=${startDateIso}&end_date=${endDateIso}`,
|
||||
`/v1/reporting/transactions?start_date=${startDateIso}&end_date=${endDateIso}&fields=all`,
|
||||
{}
|
||||
);
|
||||
|
||||
const returnTransactions: PayPalTransaction[] = []
|
||||
const returnTransactions: PayPalTransaction[] = [];
|
||||
for (const transactionDetail of response.transaction_details) {
|
||||
const apiObject: IPayPalOriginTransactionApiObject = transactionDetail.transaction_info;
|
||||
const apiObject: IPayPalOriginTransactionApiObject = transactionDetail;
|
||||
const paypalTransaction = new PayPalTransaction({
|
||||
originApiObject: apiObject,
|
||||
id: apiObject.transaction_id,
|
||||
amount: parseFloat(apiObject.transaction_amount.value),
|
||||
currency: apiObject.transaction_amount.currency_code as 'EUR' | 'USD',
|
||||
timestampIso: apiObject.transaction_initiation_date
|
||||
id: apiObject.transaction_info.transaction_id,
|
||||
amount: parseFloat(apiObject.transaction_info.transaction_amount.value),
|
||||
currency: apiObject.transaction_info.transaction_amount.currency_code as 'EUR' | 'USD',
|
||||
timestampIso: apiObject.transaction_info.transaction_initiation_date,
|
||||
name: `${apiObject.payer_info.payer_name.alternate_full_name} (${apiObject.payer_info.email_address})`,
|
||||
description: `${apiObject.cart_info?.item_details?.length} items: ${apiObject.cart_info?.item_details?.map(itemArg => {
|
||||
return `${itemArg.item_name}`;
|
||||
}).reduce((accumulatorArg, currentValue) => {
|
||||
let returnString = '';
|
||||
if (accumulatorArg) {
|
||||
returnString = accumulatorArg + ', ' + currentValue;
|
||||
} else {
|
||||
returnString = currentValue;
|
||||
}
|
||||
return returnString;
|
||||
})}`,
|
||||
});
|
||||
returnTransactions.push(paypalTransaction);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user