diff --git a/samples/payment/InitCheckoutCardVerify.js b/samples/payment/InitCheckoutCardVerify.js new file mode 100644 index 0000000..7fb3a13 --- /dev/null +++ b/samples/payment/InitCheckoutCardVerify.js @@ -0,0 +1,19 @@ +const Craftgate = require('../../dist'); + +const craftgate = new Craftgate.Client({ + apiKey: 'api-key', + secretKey: 'secret-key', + baseUrl: 'https://sandbox-api.craftgate.io' +}); + +const request = { + verificationPrice: 10, + currency: Craftgate.Model.Currency.TRY, + conversationId: '456d1297-908e-4bd6-a13b-4be31a6e47d5', + callbackUrl: 'https://www.your-website.com/craftgate-checkout-card-verify-callback', + paymentAuthenticationType: Craftgate.Model.CardVerificationAuthType.NonThreeDs +}; + +craftgate.payment().initCheckoutCardVerify(request) + .then(result => console.info('Init checkout card verify successful', result)) + .catch(err => console.error('Init checkout card verify failed', err)); diff --git a/samples/payment/VerifyCard.js b/samples/payment/VerifyCard.js new file mode 100644 index 0000000..51bf84d --- /dev/null +++ b/samples/payment/VerifyCard.js @@ -0,0 +1,28 @@ +const Craftgate = require('../../dist'); + +const craftgate = new Craftgate.Client({ + apiKey: 'api-key', + secretKey: 'secret-key', + baseUrl: 'https://sandbox-api.craftgate.io' +}); + +const request = { + card: { + cardHolderName: 'Haluk Demir', + cardNumber: '5258640000000001', + expireYear: '2044', + expireMonth: '07', + cvc: '000', + cardAlias: 'My YKB Card' + }, + paymentAuthenticationType: Craftgate.Model.CardVerificationAuthType.ThreeDs, + callbackUrl: 'https://www.your-website.com/craftgate-3DSecure-card-verify-callback', + conversationId: '456d1297-908e-4bd6-a13b-4be31a6e47d5', + verificationPrice: 10, + currency: Craftgate.Model.Currency.TRY, + clientIp: '127.0.0.1' +}; + +craftgate.payment().verifyCard(request) + .then(result => console.info('Verify card successful', result)) + .catch(err => console.error('Verify card failed', err)); diff --git a/src/adapter/PaymentAdapter.ts b/src/adapter/PaymentAdapter.ts index a1cf8d3..2d084b9 100644 --- a/src/adapter/PaymentAdapter.ts +++ b/src/adapter/PaymentAdapter.ts @@ -17,6 +17,7 @@ import DisapprovePaymentTransactionsRequest from '../request/DisapprovePaymentTr import InitApmDepositPaymentRequest from '../request/InitApmDepositPaymentRequest'; import InitApmPaymentRequest from '../request/InitApmPaymentRequest'; import InitBnplPaymentRequest from '../request/InitBnplPaymentRequest'; +import InitCheckoutCardVerifyRequest from '../request/InitCheckoutCardVerifyRequest'; import InitCheckoutPaymentRequest from '../request/InitCheckoutPaymentRequest'; import InitGarantiPayPaymentRequest from '../request/InitGarantiPayPaymentRequest'; import InitPosApmPaymentRequest from '../request/InitPosApmPaymentRequest'; @@ -32,6 +33,7 @@ import SearchStoredCardsRequest from '../request/SearchStoredCardsRequest'; import StoreCardRequest from '../request/StoreCardRequest'; import UpdateCardRequest from '../request/UpdateCardRequest'; import UpdatePaymentTransactionRequest from '../request/UpdatePaymentTransactionRequest'; +import VerifyCardRequest from '../request/VerifyCardRequest'; import ApmDepositPaymentResponse from '../response/ApmDepositPaymentResponse'; import BnplPaymentOfferResponse from '../response/BnplPaymentOfferResponse'; @@ -42,6 +44,7 @@ import DepositPaymentResponse from '../response/DepositPaymentResponse'; import FundTransferDepositPaymentResponse from '../response/FundTransferDepositPaymentResponse'; import InitApmPaymentResponse from '../response/InitApmPaymentResponse'; import InitBnplPaymentResponse from '../response/InitBnplPaymentResponse'; +import InitCheckoutCardVerifyResponse from '../response/InitCheckoutCardVerifyResponse'; import InitCheckoutPaymentResponse from '../response/InitCheckoutPaymentResponse'; import InitGarantiPayPaymentResponse from '../response/InitGarantiPayPaymentResponse'; import InitPosApmPaymentResponse from '../response/InitPosApmPaymentResponse'; @@ -54,8 +57,10 @@ import PaymentTransactionApprovalListResponse from '../response/PaymentTransacti import PaymentTransactionRefundListResponse from '../response/PaymentTransactionRefundListResponse'; import PaymentTransactionRefundResponse from '../response/PaymentTransactionRefundResponse'; import PaymentTransactionResponse from '../response/PaymentTransactionResponse'; +import RetrieveCheckoutCardVerifyResponse from '../response/RetrieveCheckoutCardVerifyResponse'; import RetrieveLoyaltiesResponse from '../response/RetrieveLoyaltiesResponse'; import StoredCardResponse from '../response/StoredCardResponse'; +import VerifyCardResponse from '../response/VerifyCardResponse'; import WaitingPaymentRefundResponse from '../response/WaitingPaymentRefundResponse'; import BaseAdapter from './BaseAdapter'; @@ -89,6 +94,14 @@ export default class PaymentAdapter extends BaseAdapter { return this._client.post('/payment/v1/checkout-payments/init', request); } + async initCheckoutCardVerify(request: InitCheckoutCardVerifyRequest): Promise { + return this._client.post('/payment/v1/checkout-card-verify/init', request); + } + + async retrieveCheckoutCardVerify(token: string): Promise { + return this._client.get(`/payment/v1/checkout-card-verify/${token}`); + } + async retrieveCheckoutPayment(token: string): Promise { return this._client.get(`/payment/v1/checkout-payments/${token}`); } @@ -189,6 +202,10 @@ export default class PaymentAdapter extends BaseAdapter { await this._client.post('/payment/v1/cards/delete', request); } + async verifyCard(request: VerifyCardRequest): Promise { + return this._client.post('/payment/v1/cards/verify', request); + } + async approvePaymentTransactions(request: ApprovePaymentTransactionsRequest): Promise { return this._client.post('/payment/v1/payment-transactions/approve', request); } diff --git a/src/model/CardVerificationAuthType.ts b/src/model/CardVerificationAuthType.ts new file mode 100644 index 0000000..d4232c6 --- /dev/null +++ b/src/model/CardVerificationAuthType.ts @@ -0,0 +1,7 @@ +enum CardVerificationAuthType { + NonThreeDs = 'NON_THREE_DS', + ThreeDs = 'THREE_DS', + None = 'NONE' +} + +export default CardVerificationAuthType; diff --git a/src/model/CardVerifyStatus.ts b/src/model/CardVerifyStatus.ts new file mode 100644 index 0000000..f0054f0 --- /dev/null +++ b/src/model/CardVerifyStatus.ts @@ -0,0 +1,7 @@ +enum CardVerifyStatus { + Success = 'SUCCESS', + Failure = 'FAILURE', + ThreeDsPending = 'THREE_DS_PENDING' +} + +export default CardVerifyStatus; diff --git a/src/model/index.ts b/src/model/index.ts index 77f777d..9be4e6e 100644 --- a/src/model/index.ts +++ b/src/model/index.ts @@ -7,6 +7,8 @@ import BounceStatus from './BounceStatus'; import CardAssociation from './CardAssociation'; import CardBrand from './CardBrand'; import CardExpiryStatus from './CardExpiryStatus'; +import CardVerificationAuthType from './CardVerificationAuthType'; +import CardVerifyStatus from './CardVerifyStatus'; import CardType from './CardType'; import ClientType from './ClientType'; import Currency from './Currency'; @@ -111,5 +113,7 @@ export = { PosUserType, PosOperationType, CardBrand, + CardVerificationAuthType, + CardVerifyStatus, ClientType }; diff --git a/src/request/InitCheckoutCardVerifyRequest.ts b/src/request/InitCheckoutCardVerifyRequest.ts new file mode 100644 index 0000000..8d5fa62 --- /dev/null +++ b/src/request/InitCheckoutCardVerifyRequest.ts @@ -0,0 +1,14 @@ +import CardVerificationAuthType from '../model/CardVerificationAuthType'; +import Currency from '../model/Currency'; + +type InitCheckoutCardVerifyRequest = { + verificationPrice: number; + currency: Currency; + conversationId?: string; + callbackUrl: string; + cardUserKey?: string; + paymentAuthenticationType: CardVerificationAuthType; + ttl?: number; +}; + +export default InitCheckoutCardVerifyRequest; diff --git a/src/request/VerifyCardRequest.ts b/src/request/VerifyCardRequest.ts new file mode 100644 index 0000000..e91191a --- /dev/null +++ b/src/request/VerifyCardRequest.ts @@ -0,0 +1,16 @@ +import CardVerificationAuthType from '../model/CardVerificationAuthType'; +import Currency from '../model/Currency'; + +import VerifyCard from './dto/VerifyCard'; + +type VerifyCardRequest = { + card: VerifyCard; + paymentAuthenticationType: CardVerificationAuthType; + verificationPrice: number; + currency: Currency; + clientIp?: string; + conversationId?: string; + callbackUrl?: string; +}; + +export default VerifyCardRequest; diff --git a/src/request/dto/VerifyCard.ts b/src/request/dto/VerifyCard.ts new file mode 100644 index 0000000..e892652 --- /dev/null +++ b/src/request/dto/VerifyCard.ts @@ -0,0 +1,11 @@ +type VerifyCard = { + cardHolderName: string; + cardNumber: string; + expireYear: string; + expireMonth: string; + cvc: string; + cardAlias?: string; + cardUserKey?: string; +}; + +export default VerifyCard; diff --git a/src/response/InitCheckoutCardVerifyResponse.ts b/src/response/InitCheckoutCardVerifyResponse.ts new file mode 100644 index 0000000..07045bb --- /dev/null +++ b/src/response/InitCheckoutCardVerifyResponse.ts @@ -0,0 +1,7 @@ +type InitCheckoutCardVerifyResponse = { + token: string; + pageUrl: string; + tokenExpireDate: Date; +}; + +export default InitCheckoutCardVerifyResponse; diff --git a/src/response/RetrieveCheckoutCardVerifyResponse.ts b/src/response/RetrieveCheckoutCardVerifyResponse.ts new file mode 100644 index 0000000..52d2a0f --- /dev/null +++ b/src/response/RetrieveCheckoutCardVerifyResponse.ts @@ -0,0 +1,8 @@ +import StoredCardResponse from './StoredCardResponse'; + +type RetrieveCheckoutCardVerifyResponse = { + token: string; + card: StoredCardResponse | null; +}; + +export default RetrieveCheckoutCardVerifyResponse; diff --git a/src/response/VerifyCardResponse.ts b/src/response/VerifyCardResponse.ts new file mode 100644 index 0000000..f9e33b8 --- /dev/null +++ b/src/response/VerifyCardResponse.ts @@ -0,0 +1,14 @@ +import CardVerifyStatus from '../model/CardVerifyStatus'; +import RefundStatus from '../model/RefundStatus'; + +type VerifyCardResponse = { + cardUserKey: string; + cardToken: string; + htmlContent: string; + redirectUrl: string; + merchantCallbackUrl: string; + refundStatus: RefundStatus; + cardVerifyStatus: CardVerifyStatus; +}; + +export default VerifyCardResponse; diff --git a/test/adapter/PaymentAdapter.test.js b/test/adapter/PaymentAdapter.test.js index f703010..187d799 100644 --- a/test/adapter/PaymentAdapter.test.js +++ b/test/adapter/PaymentAdapter.test.js @@ -1195,4 +1195,83 @@ test('should verify 3ds callback hash with all information', async t => { const isVerified = await paymentAdapter.is3DSecureCallbackVerified(threeDSecureCallbackKey, params); t.is(isVerified, true) +}); + +test('should init checkout card verify', async t => { + const scope = nock('http://localhost:8000') + .post('/payment/v1/checkout-card-verify/init') + .reply(200, { + data: { + token: 'd1811bb0-25a2-40c7-ba71-c8b605259611', + pageUrl: 'http://localhost:8070?token=d1811bb0-25a2-40c7-ba71-c8b605259611', + tokenExpireDate: '2024-07-07T00:00:00' + } + }); + + const request = { + verificationPrice: 10, + currency: Craftgate.Model.Currency.TRY, + callbackUrl: 'https://www.your-website.com/craftgate-checkout-card-verify-callback', + conversationId: 'foo-bar', + paymentAuthenticationType: Craftgate.Model.CardVerificationAuthType.NonThreeDs + }; + + const result = await paymentAdapter.initCheckoutCardVerify(request); + t.is(result.token, 'd1811bb0-25a2-40c7-ba71-c8b605259611'); + t.is(result.pageUrl, 'http://localhost:8070?token=d1811bb0-25a2-40c7-ba71-c8b605259611'); + t.truthy(result.tokenExpireDate); +}); + +test('should retrieve checkout card verify', async t => { + const scope = nock('http://localhost:8000') + .get('/payment/v1/checkout-card-verify/d1811bb0-25a2-40c7-ba71-c8b605259611') + .reply(200, { + data: { + token: 'd1811bb0-25a2-40c7-ba71-c8b605259611', + card: { + cardUserKey: 'de050909-39a9-473c-a81a-f186dd55cfef', + cardToken: 'f13129c4-55b2-4055-8c94-60c0d8c51a3b' + } + } + }); + + const result = await paymentAdapter.retrieveCheckoutCardVerify('d1811bb0-25a2-40c7-ba71-c8b605259611'); + t.is(result.token, 'd1811bb0-25a2-40c7-ba71-c8b605259611'); + t.is(result.card.cardUserKey, 'de050909-39a9-473c-a81a-f186dd55cfef'); + t.is(result.card.cardToken, 'f13129c4-55b2-4055-8c94-60c0d8c51a3b'); +}); + +test('should verify card with 3ds', async t => { + const scope = nock('http://localhost:8000') + .post('/payment/v1/cards/verify') + .reply(200, { + data: { + cardVerifyStatus: 'THREE_DS_PENDING', + htmlContent: 'PGh0bWw+M2RzZWN1cmUgaHRtbDwvaHRtbD4=', + cardUserKey: 'de050909-39a9-473c-a81a-f186dd55cfef', + cardToken: 'f13129c4-55b2-4055-8c94-60c0d8c51a3b', + refundStatus: 'WAITING' + } + }); + + const request = { + card: { + cardHolderName: 'Haluk Demir', + cardNumber: '5258640000000001', + expireYear: '2044', + expireMonth: '07', + cvc: '000', + cardAlias: 'My YKB Card' + }, + paymentAuthenticationType: Craftgate.Model.CardVerificationAuthType.ThreeDs, + callbackUrl: 'https://www.your-website.com/craftgate-3DSecure-card-verify-callback', + conversationId: 'foo-bar', + verificationPrice: 10, + currency: Craftgate.Model.Currency.TRY, + clientIp: '127.0.0.1' + }; + + const result = await paymentAdapter.verifyCard(request); + t.is(result.cardVerifyStatus, Craftgate.Model.CardVerifyStatus.ThreeDsPending); + t.truthy(result.htmlContent); }); \ No newline at end of file