Skip to content

Commit

Permalink
Merge pull request #5163 from DivanteLtd/sofort-cko
Browse files Browse the repository at this point in the history
Sofort CKO
  • Loading branch information
Fifciu committed Nov 16, 2020
2 parents fef5eb0 + 7a5c3c4 commit fc33ab1
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 10 deletions.
14 changes: 6 additions & 8 deletions packages/checkout-com/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,19 +121,17 @@ onMounted(async () => {
await loadAvailableMethods(cart.value.id, user.value && user.value.email);
})
```
5. Execute `initForm`. It mounts different payment handlers depends on arguments (check details below). If you are calling it after load component - **use `onMounted` to make sure DOM Element where it should be mounted already exists**. Card's Frames will be mounted in DOM element with class `card-frame`. Caution: PayPal does not need any SDK, we just redirect to their's website like in 3DS redirection process for credit cards. So if you are interested only in this payment method you could omit this step.
5. Execute `initForm`. It mounts different payment handlers depends on arguments (check details below). If you are calling it after load component - **use `onMounted` to make sure DOM Element where it should be mounted already exists**. Card's Frames will be mounted in DOM element with class `card-frame`. Caution: PayPal and Sofort do not need any SDK, we just redirect to their's website like in 3DS redirection process for credit cards. So if you are interested only in this payment method you could omit this step.

```ts
interface PaymentMethods {
card?: boolean;
klarna?: boolean;
paypal?: boolean;
}

interface PaymentMethodsConfig {
card?: Omit<Configuration, 'publicKey'>;
klarna?: any;
paypal?: any;
}

const initForm = (initMethods: PaymentMethods = null, config: PaymentMethodsConfig = {}): void
Expand Down Expand Up @@ -192,15 +190,15 @@ if (error.value) {
const order = await placeOrder();
```

12. `payment.data.redirect_url` contains 3DS Auth redirect url for Credit Card if it requires it and it always contain redirect url for the PayPal. You have to support it:
12. `payment.data.redirect_url` contains 3DS Auth redirect url for Credit Card if it requires it and it always contain redirect url for the PayPal and Sofort. You have to support it:
```js
if (payment.data.redirect_url) {
window.location.href = payment.data.redirect_url;
return;
}
```

13. After 3DS Auth/PayPal Auth, user will be redirected to one of these urls. They are being created inside `makePayment` method:
13. After 3DS Auth/PayPal Auth/Sofort Auth, user will be redirected to one of these urls. They are being created inside `makePayment` method:
```js
success_url: `${window.location.origin}/cko/payment-success`,
failure_url: `${window.location.origin}/cko/payment-error`
Expand Down Expand Up @@ -266,7 +264,8 @@ enum CkoPaymentType {
CREDIT_CARD = 1,
SAVED_CARD,
KLARNA, // Not supported yet
PAYPAL
PAYPAL,
SOFORT
}
```

Expand Down Expand Up @@ -311,13 +310,12 @@ const savePaymentInstrument = ref(loadSavePaymentInstrument());
```

## Autoloading SDK
Checkout.com supports 3 payment methods - Credit Card, Klarna & Paypal. By default, module fetches SDK only for Credit Card (Frames). You can customize it with module's config `paymentMethods` attribute. E.g:
Checkout.com supports many payment methods, only a few have own SDKs - Credit Card & Klarna. By default, module fetches SDK only for Credit Card (Frames). You can customize it with module's config `paymentMethods` attribute. E.g:
```js
['@vue-storefront/checkout-com/nuxt', {
// ...
paymentMethods: {
cc: true,
paypal: false,
klarna: true
}
}]
Expand Down
171 changes: 171 additions & 0 deletions packages/checkout-com/__tests__/useCkoSofort.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import useCkoSofort from '../src/useCkoSofort';
import { createContext, createPayment } from '../src/payment';
import { getCurrentPaymentMethodPayload, CkoPaymentType } from '../src/helpers';

const defaultPaymentResponse = {
status: 200
};
jest.mock('../src/payment', () => ({
createContext: jest.fn(() => Promise.resolve({
data: {
id: '12'
}
})),
createPayment: jest.fn(() => Promise.resolve(defaultPaymentResponse))
}));
jest.mock('../src/helpers', () => ({
getCurrentPaymentMethodPayload: jest.fn(),
CkoPaymentType: jest.requireActual('../src/helpers').CkoPaymentType
}));

const {
makePayment,
error
} = useCkoSofort();

describe('[checkout-com] useCkoSofort', () => {

beforeEach(() => {
jest.clearAllMocks();
});

it('does not create context if provided', async () => {

/*eslint-disable */
const reference = {
cartId: 15,
email: 'ab@gmail.com',
contextDataId: 'provided-id',
success_url: null,
failure_url: null
};
/* eslint-enable */

await makePayment(reference);

expect(createContext).not.toHaveBeenCalled();

});

it('creates context if not provided', async () => {

/*eslint-disable */
const payload = {
cartId: 15,
email: 'ab@gmail.com',
success_url: null,
failure_url: null
};
/* eslint-enable */

const expectedPayload = {
reference: payload.cartId,
email: payload.email
};

await makePayment(payload);

expect(createContext).toHaveBeenCalledWith(expectedPayload);

});

it('calls createPayment & returns proper success response', async () => {

/*eslint-disable */
const payload = {
cartId: 15,
contextDataId: 'abc',
email: 'ab@gmail.com',
success_url: null,
failure_url: null
};
/* eslint-enable */

const response = await makePayment(payload);

expect(createPayment).toHaveBeenCalled();
expect(response).toEqual(defaultPaymentResponse);

});

it('uses default values for success and failure url and reference', async () => {

/*eslint-disable */
const payload = {
cartId: 15,
contextDataId: 'abc',
email: 'ab@gmail.com',
reference: 'zyxxzxz'
};

const exptectedObject = {
context_id: payload.contextDataId,
success_url: `${window.location.origin}/cko/payment-success`,
failure_url: `${window.location.origin}/cko/payment-error`,
reference: 'zyxxzxz'
}
/* eslint-enable */

const response = await makePayment(payload);

expect(getCurrentPaymentMethodPayload).toHaveBeenCalledWith(CkoPaymentType.SOFORT, exptectedObject);
expect(response).toEqual(defaultPaymentResponse);

});

it('allows to set success and failure url', async () => {

/*eslint-disable */
const payload = {
cartId: 15,
contextDataId: 'abc',
email: 'ab@gmail.com',
success_url: 'aa',
failure_url: 'bb'
};

const expectedObject = {
context_id: payload.contextDataId,
success_url: payload.success_url,
failure_url: payload.failure_url,
reference: null
}
/* eslint-enable */

const response = await makePayment(payload);

expect(getCurrentPaymentMethodPayload).toHaveBeenCalledWith(CkoPaymentType.SOFORT, expectedObject);
expect(response).toEqual(defaultPaymentResponse);

});

it('throws an error if receives diff. code than 200 and 202 from createPayment request', async () => {

/*eslint-disable */
const payload = {
cartId: 15,
contextDataId: 'abc',
email: 'ab@gmail.com',
success_url: null,
failure_url: null
};

const errorValue = 'Some error';

(createPayment as jest.Mock).mockImplementation(() => Promise.resolve({
status: 400,
data: {
error_type: errorValue
}
}))
/* eslint-enable */

const response = await makePayment(payload);

expect(createPayment).toHaveBeenCalled();
expect(error.value.message).toBe(errorValue);
expect(response).toBe(null);

});

});
1 change: 0 additions & 1 deletion packages/checkout-com/nuxt/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import proxyMiddleware from '@vue-storefront/checkout-com/nuxt/proxyMiddleware';

const defaultPaymentMethods = {
cc: true,
paypal: true,
klarna: false
};

Expand Down
7 changes: 6 additions & 1 deletion packages/checkout-com/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ enum CkoPaymentType {
CREDIT_CARD = 1,
SAVED_CARD,
KLARNA,
PAYPAL
PAYPAL,
SOFORT
}

const buildBasePaymentMethodPayload = ({ context_id, save_payment_instrument, secure3d, success_url, failure_url, cvv, reference }: PaymentPropeties) => ({
Expand Down Expand Up @@ -75,6 +76,10 @@ const buildPaymentPayloadStrategies = {
[CkoPaymentType.PAYPAL]: (properties: PaymentPropeties): PaymentMethodPayload => ({
...buildBasePaymentMethodPayload(properties),
type: 'paypal'
}),
[CkoPaymentType.SOFORT]: (properties: PaymentPropeties): PaymentMethodPayload => ({
...buildBasePaymentMethodPayload(properties),
type: 'sofort'
})
};

Expand Down
9 changes: 9 additions & 0 deletions packages/checkout-com/src/useCko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ref, computed } from '@vue/composition-api';
import { CkoPaymentType } from './helpers';
import useCkoCard from './useCkoCard';
import useCkoPaypal from './useCkoPaypal';
import useCkoSofort from './useCkoSofort';

const error = ref(null);
const availableMethods = ref([]);
Expand Down Expand Up @@ -53,6 +54,11 @@ const useCko = () => {
error: paypalError
} = useCkoPaypal();

const {
makePayment: makeSofortPayment,
error: sofortError
} = useCkoSofort();

const loadAvailableMethods = async (reference, email?) => {
try {
const response = await createContext({ reference, email });
Expand Down Expand Up @@ -117,6 +123,9 @@ const useCko = () => {
} else if (selectedPaymentMethod.value === CkoPaymentType.PAYPAL) {
finalizeTransactionFunction = makePaypalPayment;
localError = paypalError;
} else if (selectedPaymentMethod.value === CkoPaymentType.SOFORT) {
finalizeTransactionFunction = makeSofortPayment;
localError = sofortError;
} else {
error.value = new Error('Not supported payment method');
return;
Expand Down
49 changes: 49 additions & 0 deletions packages/checkout-com/src/useCkoSofort.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/* eslint-disable camelcase, @typescript-eslint/camelcase */

import { createContext, createPayment } from './payment';
import { ref } from '@vue/composition-api';
import { CkoPaymentType, getCurrentPaymentMethodPayload } from './helpers';

const error = ref(null);

const useCkoSofort = () => {
const makePayment = async ({
cartId,
email,
contextDataId = null,
success_url = null,
failure_url = null,
reference = null
}) => {
try {
let context;
if (!contextDataId) {
context = await createContext({ reference: cartId, email });
}

const payment = await createPayment(
getCurrentPaymentMethodPayload(CkoPaymentType.SOFORT, {
reference,
context_id: contextDataId || context.data.id,
success_url: success_url || `${window.location.origin}/cko/payment-success`,
failure_url: failure_url || `${window.location.origin}/cko/payment-error`
})
);

if (![200, 202].includes(payment.status)) {
throw new Error(payment.data.error_type);
}

return payment;
} catch (e) {
error.value = e;
return null;
}
};

return {
error,
makePayment
};
};
export default useCkoSofort;
3 changes: 3 additions & 0 deletions packages/core/docs/checkout-com/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## 0.0.9 (not released)
- Sofort support ([#5158](https://github.com/DivanteLtd/vue-storefront/issues/5158))

## 0.0.8

- Fixed proxied endpoints URLs ([#5117](https://github.com/DivanteLtd/vue-storefront/pull/5117))
Expand Down

0 comments on commit fc33ab1

Please sign in to comment.