Links
Comment on page

Custom payment flow

Inpage checkout

1. Set up MoneyCollect

Use our official libraries for access to the MoneyCollect API from your application:
<dependency>
<groupId>com.moneycollect</groupId>
<artifactId>moneycollect-java</artifactId>
<version>{VERSION}</version>
</dependency>

2. Create a Payment

MoneyCollect uses a Payment object to represent your intent to collect payment from a customer, tracking charge attempts and payment state changes throughout the process.
Handle any next actions
Add an endpoint on your server that creates a Payment. A Payment tracks the customer’s payment life cycle, keeping track of any failed payment attempts and ensuring the customer is only charged once. Return the Payment’s client secret in the response to finish the payment on the client.
package com.moneycollect.sample;
import com.alibaba.fastjson.JSONObject;
import com.moneycollect.Moneycollect;
import com.moneycollect.model.Payment;
import com.moneycollect.param.PaymentCreateParams;
import lombok.Data;
import java.nio.file.Paths;
import static spark.Spark.*;
public class Server {
private static JSONObject json = new JSONObject();
@Data
static class CreatePaymentResponse {
private String id;
private String clientSecret;
public CreatePaymentResponse(String id,String clientSecret) {
this.id = id;
this.clientSecret = clientSecret;
}
}
public static void main(String[] args) {
port(5050);
staticFiles.externalLocation(Paths.get("public").toAbsolutePath().toString());
// This is a sample test API key.
MoneyCollect.apiKey = "test_pr_K***";
post("/create-payment", (request, response) -> {
response.type("application/json");
PaymentCreateParams params =
PaymentCreateParams.builder()
.setAmount(14*100L)
.setCurrency("USD")
.setOrderNo("MC"+System.currentTimeMillis())
.build();
Payment payment = Payment.create(params);
CreatePaymentResponse paymentResponse = new CreatePaymentResponse(payment.getId(),payment.getClientSecret());
return json.toJSONString(paymentResponse);
});
}
}

3. Build a checkout page on the client

Load MoneyCollect.js Use McPayment.js to remain PCI compliant by ensuring that payment details are sent directly to MoneyCollect without hitting your server. Always load MoneyCollect.js from static.moneycollect.com to remain compliant. Don’t include the script in a bundle or host it yourself.
Define the payment form
Add an empty placeholder div to your checkout form. MoneyCollect inserts an iframe into this div that securely collects payment information.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Accept a payment</title>
<meta name="description" content="A demo of a payment on MoneyCollect" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="checkout.css" />
</head>
<body>
<div class="sr-root" style="margin: auto;">
<div class="payment-view" >
<!-- Display a payment form -->
<form id="payment-form">
<div id="card-element" class="ab-element">
<!--MoneyCollect.js injects the Payment Element-->
</div>
<div class="sr-field-error" id="card-errors" role="alert"></div>
<button id="submit">
<div class="spinner hidden" id="spinner"></div>
<span id="button-text">Pay now</span>
</button>
<div id="payment-message" class="hidden" style="color:#ff0000"></div>
</form>
</div>
</div>
<script src="https://static.moneycollect.com/jssdk/js/MoneyCollect.js"></script>
<script src="checkout.js" defer></script>
</body>
</html>
Initialize MoneyCollect.js
Initialize MoneyCollect.js with your publishable API keys. You will use MoneyCollect.js to create the Payment Element and complete the payment on the client.
Fetch a Payment
Immediately make a request to the endpoint on your server to create a new Payment as soon as your checkout page loads. The clientSecret,id returned by your endpoint is used to complete the payment.
Initialize MoneyCollect Elements
Initialize the MoneyCollect Elements UI library with the client secret. Elements manages the UI components you need to collect payment details.
// A reference to MoneyCollect.js initialized with a fake API key.
var apikey = "API Public key";
var moneyCollect;
// The items the customer wants to buy
var items = [{ id: "xl-tshirt" }];
initialize();
// Fetches a payment intent and captures the client secret
async function initialize() {
const response = await fetch("/create-payment", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ items }),
});
var paymentResponse = await response.json();
setupElements();
document.querySelector("#payment-form")
.addEventListener("submit", function(evt) {
evt.preventDefault();
// Initiate payment
handleSubmit(paymentResponse);
});
}
async function setupElements() {
moneyCollect = MoneycollectPay(apikey);
let params = {
formId: 'payment-form', // form id
frameId: 'PrivateFrame', // generated IframeId
mode: "test",
customerId: "",
needCardList: true,
autoValidate: true,
modelType: "normal",
lang: 'ko', // Form display and verification information language
layout: {
pageMode: "inner", // Style mode of the page inner | block
style: {
frameMaxHeight: "50", // The max height of iframe
input: {
FontSize: "", // Collect the size of the font
FontFamily: "", // Specify a prioritized list of one or more font family names
FontWeight: "", // Collect the weight (or boldness) of the font
Color: "", // Collect the foreground color value of an element's text
ContainerBorder: "", //Collect the name of the font
ContainerBg: "", // Collect the weight (or boldness) of the font
ContainerSh: "" //Collect the color of the font
}
}
}
}
moneyCollect.elementInit("payment_steps", params);
}
// Billing Address
let paymentMethodsData = {
billingDetails: {
address: {
city: "Hong Kong",
country: "CN",
line1: "193 Prince Edward Road",
line2: "",
postalCode: "421455",
state: "Hong Kong"
},
firstName: "su",
lastName: "diana",
phone: "19112454541"
}
}
async function handleSubmit(paymentResponse) {
setLoading(true);
moneyCollect.confirmPayment({
paymentMethod: paymentMethodsData,
autoJump: false,
payment_id: paymentResponse.id,
clientSecret: paymentResponse.clientSecret,
confirmDetail: {} //Confirm parameters
}).then((result) => {
if(result.data.code === "success"){
orderComplete(result.data.data);
} else {
showMessage(result.data.msg);
}
});
setLoading(false);
}
// Fetches the payment intent status after payment submission
async function orderComplete(paymentIntent) {
document.querySelectorAll(".payment-view").forEach(function(view) {
view.classList.add("hidden");
});
document.querySelectorAll(".completed-view").forEach(function(view) {
view.classList.remove("hidden");
});
document.querySelector(".status").textContent = paymentIntent.status ;
var paymentIntentJson = JSON.stringify(paymentIntent, null, 2);
document.querySelector("pre").textContent = paymentIntentJson;
}
// ------- UI helpers -------
function showMessage(messageText) {
const messageContainer = document.querySelector("#payment-message");
messageContainer.classList.remove("hidden");
messageContainer.textContent = messageText;
}
// Show a spinner on payment submission
function setLoading(isLoading) {
if (isLoading) {
// Disable the button and show a spinner
document.querySelector("#submit").disabled = true;
document.querySelector("#spinner").classList.remove("hidden");
document.querySelector("#button-text").classList.add("hidden");
} else {
document.querySelector("#submit").disabled = false;
document.querySelector("#spinner").classList.add("hidden");
document.querySelector("#button-text").classList.remove("hidden");
}
}
Customlize your payment page language
You can customize the page language through setting the parameterlang .
Currently supports:
Japanese(ja), Korean(ko), English(en), Traditional Chinese-Taiwan(ch-TW), German(pt_de), Spanish(pt_es), French(pt_fr), Italian(pt_it), Portuguese-Brazil(pt_Br), Portugal-Portugal(pt_PT), Russian(ru);
e.g.
lang: 'ko'// Set form language to Korean
If the current browser language is one of the supported languages, the language will be displayed; otherwise, English will be displayed by default
/* Variables */
* {
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
font-size: 16px;
-webkit-font-smoothing: antialiased;
display: flex;
justify-content: center;
align-content: center;
height: 100vh;
width: 100vw;
}
form {
width: 30vw;
min-width: 500px;
align-self: center;
box-shadow: 0px 0px 0px 0.5px rgba(50, 50, 93, 0.1),
0px 2px 5px 0px rgba(50, 50, 93, 0.1), 0px 1px 1.5px 0px rgba(0, 0, 0, 0.07);
border-radius: 7px;
padding: 40px;
}
.hidden {
display: none;
}
#payment-message {
color: rgb(105, 115, 134);
font-size: 16px;
line-height: 20px;
padding-top: 12px;
text-align: center;
}
#payment-element {
margin-bottom: 24px;
}
/* Buttons and links */
button {
background: #5469d4;
font-family: Arial, sans-serif;
color: #ffffff;
border-radius: 4px;
border: 0;
padding: 12px 16px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
display: block;
transition: all 0.2s ease;
box-shadow: 0px 4px 5.5px 0px rgba(0, 0, 0, 0.07);
width: 100%;
}
button:hover {
filter: contrast(115%);
}
button:disabled {
opacity: 0.5;
cursor: default;
}
/* spinner/processing state, errors */
.spinner,
.spinner:before,
.spinner:after {
border-radius: 50%;
}
.spinner {
color: #ffffff;
font-size: 22px;
text-indent: -99999px;
margin: 0px auto;
position: relative;
width: 20px;
height: 20px;
box-shadow: inset 0 0 0 2px;
-webkit-transform: translateZ(0);
-ms-transform: translateZ(0);
transform: translateZ(0);
}
.spinner:before,
.spinner:after {
position: absolute;
content: "";
}
.spinner:before {
width: 10.4px;
height: 20.4px;
background: #5469d4;
border-radius: 20.4px 0 0 20.4px;
top: -0.2px;
left: -0.2px;
-webkit-transform-origin: 10.4px 10.2px;
transform-origin: 10.4px 10.2px;
-webkit-animation: loading 2s infinite ease 1.5s;
animation: loading 2s infinite ease 1.5s;
}
.spinner:after {
width: 10.4px;
height: 10.2px;
background: #5469d4;
border-radius: 0 10.2px 10.2px 0;
top: -0.1px;
left: 10.2px;
-webkit-transform-origin: 0px 10.2px;
transform-origin: 0px 10.2px;
-webkit-animation: loading 2s infinite ease;
animation: loading 2s infinite ease;
}
@-webkit-keyframes loading {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes loading {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@media only screen and (max-width: 600px) {
form {
width: 80vw;
min-width: initial;
}
}

4. Complete the payment on the client

Handle the submit event
Listen to the form’s submit event to know when to confirm the payment through the MoneyCollect API.
Complete the payment
Call confirmPayment(), passing along the PaymentElement and a return_url to indicate where MoneyCollect should redirect the user after they complete the payment. For payments that require authentication, MoneyCollect displays a modal for 3D Secure authentication or redirects the customer to an authentication page depending on the payment method. After the customer completes the authentication process, they’re redirected to the return_url.

5. Handle post-payment events

Use a webhook
When the notifyUrl parameter is passed, MoneyCollect will use the URL to asynchronously notify the transaction result.

6. Additional testing resources

There are several test cards you can use to make sure your integration is ready for production. Use them with any CVC, postal code, and future expiration date.
NUMBER
Brand
DESCRIPTION
4242 4242 4242 4242
Visa
Succeeds and immediately processes the payment.
3566 0020 2036 0505
JCB
Succeeds and immediately processes the payment.
6011 1111 1111 1117
Discover
Succeeds and immediately processes the payment.
3782 8224 6310 0052
American Express
Succeeds and immediately processes the payment.
5555 5555 5555 4444
Mastercard
Succeeds and immediately processes the payment.
4000 0000 0000 0077
Visa
Always fails with a decline code of declined.
4000002500003155
Visa
This card requires 3D authentication on all transactions