-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(PAYMENTS-16663): add credit card integration example
- Loading branch information
1 parent
0bf0d7b
commit 279a878
Showing
6 changed files
with
810 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
<!doctype html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<meta | ||
name="viewport" | ||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" | ||
/> | ||
<meta http-equiv="X-UA-Compatible" content="ie=edge" /> | ||
<title>Credit card integration</title> | ||
<link rel="stylesheet" href="style.css" /> | ||
|
||
<!-- | ||
Link the SDK bundle. | ||
NOTE: In this example, we use a local build just for convenience purposes. | ||
--> | ||
<script src="../../dist/main.js"></script> | ||
<!-- Initialization script --> | ||
<script src="init-payment-flow.js"></script> | ||
</head> | ||
|
||
<body> | ||
<div class="application"> | ||
<h1>Credit card integration</h1> | ||
<div class="columns-wrapper"> | ||
<div class="left-col"> | ||
<!-- | ||
Add finance details component to show purchase details | ||
--> | ||
<psdk-finance-details></psdk-finance-details> | ||
</div> | ||
|
||
<div class="right-col"> | ||
<!-- | ||
FormContainer HTML-node to implement form with all the controls | ||
--> | ||
<div id="form-container"></div> | ||
<!-- | ||
StatusContainer HTML-node to implement StatusComponent component with payment information | ||
--> | ||
<div id="status-container"></div> | ||
</div> | ||
</div> | ||
|
||
<!-- | ||
Add legal component to provide links to legal documents | ||
--> | ||
<psdk-legal></psdk-legal> | ||
</div> | ||
</body> | ||
</html> |
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,267 @@ | ||
/** | ||
* Run `buildPaymentFlow` function only when `DOMContentLoaded` to access all the DOM nodes needed. | ||
*/ | ||
document.addEventListener('DOMContentLoaded', buildPaymentFlow); | ||
|
||
function buildPaymentFlow() { | ||
if (typeof PayStationSdk === 'undefined') { | ||
alert(` | ||
It seems SDK library is undefined. | ||
Please, link CDN source or create local build (recommended to test purposes only). | ||
`); | ||
throw new Error('PayStationSdk not found'); | ||
} | ||
/** | ||
* To learn more about creating tokens, | ||
* please read https://developers.xsolla.com/api/pay-station/operation/create-token/ | ||
*/ | ||
const accessToken = 'qnntUAwBk6NOUTuIojHW3pRJvFuXwJaI_lc_ru'; | ||
|
||
if (!accessToken) { | ||
alert('No token provided. Please, check the documentation'); | ||
throw new Error('No token provided'); | ||
} | ||
|
||
/** | ||
* The SDK is available under the PayStationSdk namespace. | ||
* To begin initialization, obtain a reference to the headlessCheckout object. | ||
*/ | ||
const { headlessCheckout } = PayStationSdk; | ||
|
||
function handleRedirectAction(redirectAction) { | ||
/** | ||
* Handle redirect to 3DS secure procedure. | ||
*/ | ||
const url = new URL(redirectAction.data.redirect.redirectUrl); | ||
const params = Object.entries(redirectAction.data.redirect.data); | ||
params.forEach((entry) => { | ||
const [key, value] = entry; | ||
url.searchParams.append(key, value); | ||
}); | ||
|
||
/** | ||
* Open 3DS Secure. | ||
*/ | ||
this.window.location.href = url.toString(); | ||
} | ||
|
||
function renderRequiredFields(requiredFields, formElement) { | ||
/** | ||
* It's important to render every required field as a component according to its own type or | ||
* other specific parameters. In the current case, we could encounter fields of the following types: | ||
* 'text', 'select', or a 'text' field with the name 'card_number'. | ||
* | ||
* Every type is mapped to a suitable component, and then the component is rendered into the DOM. | ||
*/ | ||
requiredFields.forEach((field) => { | ||
if (field.type === 'text' && field.name === 'card_number') { | ||
renderCardNumberComponent(formElement, field); | ||
return; | ||
} | ||
if (field.type === 'text') { | ||
renderTextComponent(formElement, field); | ||
return; | ||
} | ||
if (field.type === 'select') { | ||
renderSelectComponent(formElement, field); | ||
return; | ||
} | ||
}); | ||
} | ||
|
||
function renderCardNumberComponent(formElement, field) { | ||
/** | ||
* You can use <psdk-card-number icon="true" name="field.name"></psdk-card-number> as well | ||
*/ | ||
const input = new PayStationSdk.CardNumberComponent(); | ||
input.setAttribute('name', field.name); | ||
formElement.append(input); | ||
} | ||
|
||
function renderSelectComponent(formElement, field) { | ||
/** | ||
* You can use <psdk-select name="field.name"></psdk-select> as well | ||
*/ | ||
const input = new PayStationSdk.SelectComponent(); | ||
input.setAttribute('name', field.name); | ||
formElement.append(input); | ||
} | ||
|
||
function renderTextComponent(formElement, field) { | ||
/** | ||
* You can use <psdk-text name="field.name"></psdk-text> as well | ||
*/ | ||
const input = new PayStationSdk.TextComponent(); | ||
input.setAttribute('name', field.name); | ||
formElement.append(input); | ||
} | ||
|
||
function renderSubmitButton(formElement) { | ||
/** | ||
* Render submit form button. | ||
* You can use <psdk-submit-button></psdk-submit-button> as well | ||
*/ | ||
const submitButton = new PayStationSdk.SubmitButtonComponent(); | ||
submitButton.setAttribute('text', 'Pay Now'); | ||
formElement.append(submitButton); | ||
} | ||
|
||
function handle3dsAction(threeDsAction) { | ||
/** | ||
* Create 3ds component. It will be handle 3ds verification flow. | ||
* You can use <psdk-3ds></psdk-3ds> component as well | ||
*/ | ||
|
||
const threeDsComponent = new PayStationSdk.ThreeDsComponent(); | ||
|
||
threeDsComponent.setAttribute( | ||
'data-challenge', | ||
JSON.stringify(threeDsAction.data.data), | ||
); | ||
|
||
document.getElementById('right-col').append(threeDsComponent); | ||
} | ||
|
||
function renderStatusComponent(statusElement) { | ||
/** | ||
* Create status component. It will be updated once payment status changed. | ||
* You can use <psdk-status></psdk-status> component as well | ||
*/ | ||
const statusComponent = new PayStationSdk.StatusComponent(); | ||
statusElement.append(statusComponent); | ||
} | ||
|
||
function clearFormFields(formElement) { | ||
/** | ||
* In some cases, we need to remove all form fields that were rendered before. | ||
* This may be necessary when processing Brazilian credit cards, because | ||
* they also have a second step with extra fields that need to be filled. | ||
* To create a fluent user experience, it would be good practice to clear fields already submitted and | ||
* render new fields in the same place. | ||
*/ | ||
formElement.innerHTML = ''; | ||
} | ||
|
||
function getRequiredFields(fields) { | ||
/** | ||
* form.fields provide available fields for selected payment method. | ||
* You can filter it by `isMandatory` flag to get required fields only | ||
*/ | ||
return fields.filter((field) => field.isMandatory === '1'); | ||
} | ||
|
||
async function initPayStationSdk() { | ||
/** | ||
* Call the `init()` method with the provided environment object. | ||
* The isWebView parameter is required and indicates whether your | ||
* integration type is a webview or not. | ||
* You can set sandbox payment mode with `sandbox` parameter | ||
* Please note that this command executes asynchronously. | ||
*/ | ||
await headlessCheckout.init({ | ||
isWebView: false, | ||
sandbox: false, | ||
returnUrl: '/', | ||
}); | ||
|
||
/** | ||
* Set styles for secure components | ||
*/ | ||
await headlessCheckout.setSecureComponentStyles(` | ||
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@500&display=swap'); | ||
input { | ||
padding: 0; | ||
border: 1px solid grey; | ||
border-radius: 8px; | ||
font-family: 'Roboto', sans-serif; | ||
font-size: 14px; | ||
height: 30px !important; | ||
} | ||
input:focus { | ||
outline: none; | ||
} | ||
`); | ||
|
||
/** | ||
* After the Payments SDK has been initialized, the next step is setting the token. | ||
* To learn more about creating tokens, | ||
* please read https://developers.xsolla.com/api/pay-station/operation/create-token/ | ||
*/ | ||
await headlessCheckout.setToken(accessToken); | ||
|
||
/** | ||
* Define payment method id. | ||
* To get lists of payment methods use psdk-payment-methods. | ||
* Please see `examples/select-method` for more details | ||
*/ | ||
const creditCardMethodId = 1380; | ||
|
||
/** | ||
* Initialize payment. | ||
* `returnUrl` will be opened after payment completed on 3DS secure side. | ||
*/ | ||
const form = await headlessCheckout.form.init({ | ||
paymentMethodId: creditCardMethodId, | ||
returnUrl: 'http://localhost:3000/return.html', | ||
}); | ||
|
||
/** | ||
* Retrieving DOM elements to render form fields and display status messages. | ||
*/ | ||
const formElement = document.querySelector('#form-container'); | ||
const statusElement = document.querySelector('#status-container'); | ||
|
||
/** | ||
* Subscribe to payment actions | ||
*/ | ||
headlessCheckout.form.onNextAction((nextAction) => { | ||
console.log('nextAction', nextAction); | ||
/** | ||
* Handle 'show_fields' action. | ||
*/ | ||
if (nextAction.type === 'show_fields') { | ||
clearFormFields(formElement); | ||
renderRequiredFields(nextAction.data.fields, formElement); | ||
renderSubmitButton(formElement); | ||
} | ||
/** | ||
* Handle 'check_status' action. | ||
*/ | ||
if (nextAction.type === 'check_status') { | ||
/** | ||
* Remove unnecessary form fields to render StatusComponent in the same place. | ||
*/ | ||
clearFormFields(formElement); | ||
renderStatusComponent(statusElement); | ||
} | ||
|
||
/** | ||
* Handle '3DS' action. | ||
*/ | ||
if (nextAction.type === '3DS') { | ||
handle3dsAction(nextAction); | ||
} | ||
|
||
/** | ||
* Handle '3DS' redirect. | ||
*/ | ||
if (nextAction.type === 'redirect') { | ||
handleRedirectAction(nextAction); | ||
} | ||
}); | ||
|
||
const requiredFields = getRequiredFields(form.fields); | ||
|
||
/** | ||
* Render requried fields | ||
*/ | ||
renderRequiredFields(requiredFields, formElement); | ||
|
||
renderSubmitButton(formElement); | ||
} | ||
|
||
// initialize sdk | ||
initPayStationSdk(); | ||
} |
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,47 @@ | ||
<!doctype html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<meta | ||
name="viewport" | ||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" | ||
/> | ||
<meta http-equiv="X-UA-Compatible" content="ie=edge" /> | ||
<title>Credit card integration</title> | ||
<link rel="stylesheet" href="style.css" /> | ||
|
||
<!-- | ||
Link the SDK bundle. | ||
NOTE: In this example, we use a local build just for convenience purposes. | ||
--> | ||
<script src="../../dist/main.js"></script> | ||
<!-- Initialization script --> | ||
<script src="return.js"></script> | ||
</head> | ||
|
||
<body> | ||
<div class="application"> | ||
<h1>Credit card integration</h1> | ||
<div class="columns-wrapper"> | ||
<div class="left-col"> | ||
<!-- | ||
Add finance details component to show purchase details | ||
--> | ||
<psdk-finance-details></psdk-finance-details> | ||
</div> | ||
|
||
<div class="right-col"> | ||
<!-- | ||
StatusComponent component with payment information | ||
--> | ||
<psdk-status></psdk-status> | ||
</div> | ||
</div> | ||
|
||
<!-- | ||
Add legal component to provide links to legal documents | ||
--> | ||
<psdk-legal></psdk-legal> | ||
</div> | ||
</body> | ||
</html> |
Oops, something went wrong.