Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
261 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
import * as t from 'io-ts'; | ||
import { BaseReceipt, LinksObject, tBaseRequestOptions, tBaseVerifyOptions } from '../../types'; | ||
|
||
/* | ||
* PayPing's API | ||
* Currency: IRT | ||
*/ | ||
|
||
export const links: LinksObject = { | ||
default: { | ||
REQUEST: 'https://api.payping.ir/v2/pay', | ||
VERIFICATION: 'https://api.payping.ir/v2/pay/verify', | ||
PAYMENT: 'https://api.payping.ir/v2/pay/gotoipg/', | ||
}, | ||
}; | ||
|
||
export interface RequestPaymentReq { | ||
/** | ||
* integer <int32> [ 100 .. 50000000 ] | ||
*/ | ||
amount: number; | ||
|
||
/** | ||
*شماره موبایل یا ایمیل پرداخت کننده | ||
*/ | ||
payerIdentity?: string; | ||
/** | ||
*نام پرداخت کننده | ||
*/ | ||
payerName?: string; | ||
/** | ||
*توضیحات | ||
*/ | ||
description?: string; | ||
/** | ||
*آدرس صفحه برگشت | ||
*/ | ||
returnUrl: string; | ||
|
||
/** | ||
*کد ارسالی توسط کاربر | ||
*/ | ||
clientRefId?: string; | ||
} | ||
|
||
export interface RequestPaymentRes { | ||
/** | ||
* کد پرداخت | ||
*/ | ||
code: string; | ||
} | ||
|
||
export interface CallbackParams { | ||
code: string; | ||
refid: string; | ||
clientrefid: string; | ||
cardnumber: string; | ||
cardhashpan: string; | ||
} | ||
|
||
export interface VerifyPaymentReq { | ||
/** | ||
* شماره فاکتور | ||
*/ | ||
refId: string; | ||
|
||
/** | ||
* مبلغ تراکنش | ||
* integer <int32> [ 100 .. 50000000 ] | ||
*/ | ||
amount: number; | ||
} | ||
|
||
export interface VerifyPaymentRes { | ||
amount: number; | ||
cardNumber: string; | ||
cardHashPan: string; | ||
} | ||
|
||
/* | ||
* Package's API | ||
*/ | ||
|
||
export const tConfig = t.intersection([ | ||
t.partial({}), | ||
t.interface({ | ||
apiKey: t.string, | ||
}), | ||
]); | ||
|
||
export type Config = t.TypeOf<typeof tConfig>; | ||
|
||
export const tRequestOptions = t.intersection([ | ||
t.partial({ | ||
mobile: t.string, | ||
email: t.string, | ||
name: t.string, | ||
}), | ||
tBaseRequestOptions, | ||
]); | ||
|
||
export type RequestOptions = t.TypeOf<typeof tRequestOptions>; | ||
|
||
export const tVerifyOptions = t.intersection([t.interface({}), tBaseVerifyOptions]); | ||
|
||
export type VerifyOptions = t.TypeOf<typeof tVerifyOptions>; | ||
|
||
export type Receipt = BaseReceipt<VerifyPaymentRes>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import { Driver } from '../../driver'; | ||
import * as API from './api'; | ||
import axios from 'axios'; | ||
import { RequestException, VerificationException } from '../../exceptions'; | ||
import { ErrorList } from '../../types'; | ||
|
||
export class PayPing extends Driver<API.Config> { | ||
constructor(config: API.Config) { | ||
super(config, API.tConfig); | ||
} | ||
|
||
protected links = API.links; | ||
|
||
requestPayment = async (options: API.RequestOptions) => { | ||
options = this.getParsedData(options, API.tRequestOptions); | ||
|
||
let { amount, callbackUrl, mobile, email, name, description } = options; | ||
let response; | ||
|
||
try { | ||
response = await axios.post<API.RequestPaymentReq, { data: API.RequestPaymentRes }>(this.getLinks().REQUEST, { | ||
amount: amount * 10, | ||
returnUrl: callbackUrl, | ||
description, | ||
payerIdentity: mobile || email, | ||
payerName: name, | ||
}); | ||
} catch (error) { | ||
throw new RequestException(this.statusToMessage((error as any).response.status)); | ||
} | ||
|
||
const { code } = response.data; | ||
|
||
return this.makeRequestInfo(code, 'GET', this.getLinks().PAYMENT + code); | ||
}; | ||
|
||
verifyPayment = async (options: API.VerifyOptions, params: API.CallbackParams): Promise<API.Receipt> => { | ||
const { code, refid } = params; | ||
const { amount } = options; | ||
let response; | ||
|
||
try { | ||
response = await axios.post<API.VerifyPaymentReq, { data: API.VerifyPaymentRes }>(this.getLinks().VERIFICATION, { | ||
amount: amount * 10, | ||
refId: code, | ||
}); | ||
} catch (error) { | ||
throw new VerificationException(this.statusToMessage((error as any).response.status)); | ||
} | ||
|
||
const { cardNumber } = response.data; | ||
|
||
return { | ||
raw: response.data, | ||
transactionId: refid, | ||
cardPan: cardNumber, | ||
}; | ||
}; | ||
|
||
statusToMessage(status = 500): string { | ||
const map: ErrorList = { | ||
'400': 'مشکلی در اطلاعات ارسالی وجود دارد.', | ||
'401': 'شما به این آیتم دسترسی ندارید.', | ||
'403': 'دسترسی شما غیر مجاز است.', | ||
'404': 'یافت نشد.', | ||
'500': 'مشکلی از طرف درگاه پرداخت رخ داده.', | ||
'502': 'سرور پراکسی با خطا مواجه شده است.', | ||
'503': 'سرور درگاه پرداخت در حال حاضر پاسخگو نیست.', | ||
}; | ||
|
||
return map[status.toString() || '500']; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import axios from 'axios'; | ||
import { PayPing } from '../src/drivers/payping'; | ||
import * as API from '../src/drivers/payping/api'; | ||
import { RequestException } from '../src/exceptions'; | ||
import { getPaymentDriver } from '../src/drivers'; | ||
|
||
jest.mock('axios'); | ||
|
||
const mockedAxios = axios as jest.Mocked<typeof axios>; | ||
describe('PayPing Driver', () => { | ||
it('returns the correct payment url', async () => { | ||
const serverResponse: API.RequestPaymentRes = { | ||
code: '1234', | ||
}; | ||
|
||
mockedAxios.post.mockResolvedValueOnce({ data: serverResponse }); | ||
|
||
const driver = getPaymentDriver<PayPing>('payping', { apiKey: '2134' }); | ||
|
||
expect( | ||
typeof (await driver.requestPayment({ callbackUrl: 'https://path.to/callback-url', amount: 20000 })).url | ||
).toBe('string'); | ||
}); | ||
|
||
it('throws payment errors accordingly', async () => { | ||
// mockedAxios.post.mockRejectedValueOnce({ response: { status: 401 } }); | ||
mockedAxios.post.mockReturnValueOnce(Promise.reject({ response: { status: 401 } })); | ||
|
||
const driver = getPaymentDriver<PayPing>('payping', { apiKey: '2134' }); | ||
|
||
await expect(async () => await driver.requestPayment({ amount: 2000, callbackUrl: 'asd' })).rejects.toThrow( | ||
RequestException | ||
); | ||
}); | ||
|
||
it('verifies the purchase correctly', async () => { | ||
const serverResponse: API.VerifyPaymentRes = { | ||
amount: 40000, | ||
cardHashPan: 'hash', | ||
cardNumber: '1234-****-****-1234', | ||
}; | ||
const expectedResult: API.Receipt = { transactionId: '1234', raw: serverResponse }; | ||
|
||
mockedAxios.post.mockResolvedValueOnce({ data: serverResponse }); | ||
|
||
const driver = getPaymentDriver<PayPing>('payping', { apiKey: '2134' }); | ||
|
||
expect( | ||
( | ||
await driver.verifyPayment( | ||
{ amount: 2000 }, | ||
{ | ||
code: '1234', | ||
cardhashpan: 'hash', | ||
clientrefid: 'clientrefid', | ||
cardnumber: '1234-****-****-1234', | ||
refid: '1234', | ||
} | ||
) | ||
).transactionId | ||
).toEqual(expectedResult.transactionId); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters