站内支付

1. 设置MoneyCollect sdk

使用我们官方的库来访问您的应用程序中的 MoneyCollect API:

<dependency>
  <groupId>com.moneycollect</groupId>
  <artifactId>moneycollect-java</artifactId>
  <version>{VERSION}</version>
</dependency>

2. 创建支付

MoneyCollect 使用支付对象来表示您计划从客户那里收款的意图,贯穿整个过程跟踪收费尝试和支付状态的改变。

处理所有后续操作

在您的服务器上添加一个端点,用于创建支付。支付追踪客户的支付生命周期,记录任何支付失败的尝试,并确保客户只被收费一次。在响应中返回支付的客户端密钥以在客户端完成支付。

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());

        // 这是一个示例测试 API 密钥。
        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. 在客户端构建结账页面

加载 MoneyCollect.js 展示信用卡付款区域,确保支付信息通过sdk收集并直接发送到 MoneyCollect,而不经过您的服务器,以保持 PCI 合规。始终从 static.moneycollect.com 加载 MoneyCollect.js 以保持合规。

不要将脚本保存在本地服务器。

定义支付表单

在您的结账表单中添加一个空白的占位符 div。MoneyCollect 会将一个 iframe 插入到这个 div 中,以便安全地收集支付信息。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>接受支付</title>
    <meta name="description" content="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" >
        <!-- 展示支付表单 -->
        <form id="payment-form">
            <div id="card-element" class="ab-element">
                <!--MoneyCollect.js注入支付元素-->
            </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">立即支付</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>

初始化 MoneyCollect.js

用您的 API 公钥初始化 MoneyCollect.js。您将使用 MoneyCollect.js 在客户端创建支付元素并完成支付。

获取支付

一旦您的结账页面加载,立即向您服务端的api发出请求来创建新的支付。您的服务端返回的 clientSecret 和 id 被用来完成后续支付。

初始化 MoneyCollect 元素

用客户端密钥初始化 MoneyCollect 元素 UI 库。Elements 管理收集付款详细信息所需的 UI 组件。

// 使用假 API 密钥初始化的 MoneyCollect.js 引用。
var apikey = "API 公开密钥";
var moneyCollect;

// 客户想要购买的物品
var items = [{ id: "xl-tshirt" }];

initialize();

// 获取支付意图并获取客户端密钥。
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();
        // 发起支付
        handleSubmit(paymentResponse);
    });
}

async function setupElements()  {
    moneyCollect = MoneycollectPay(apikey);
    let params = {
        formId: 'payment-form', // form id
        frameId: 'PrivateFrame', // 生成 IframeId
        mode: "test",
        customerId: "",
        needCardList: true,
        autoValidate: true,
        modelType: "normal",
        lang: 'ko', // 表单显示及验证信息语言
        layout: {
            pageMode: "inner", // 页面内部样式   inner | block
            style: {
                frameMaxHeight: "50", //  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);
}
// 结算地址
let paymentMethodsData = {
   billingDetails: {
        address: {
            city: "Hong Kong",
            country: "CN",
            line1: "193 Prince Edward Road",
            line2: "",
            postalCode: "421455",
            state: "Hong Kong"
        },
        email: "customer@gmail.com",
        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);
}

// 在支付提交后获取支付意图状态。
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 辅助功能 -------
function showMessage(messageText) {
    const messageContainer = document.querySelector("#payment-message");

    messageContainer.classList.remove("hidden");
    messageContainer.textContent = messageText;
}

// 在支付提交时显示加载图标。
function setLoading(isLoading) {
  if (isLoading) {
        // 禁用按钮并显示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");
    }
}

自定义您的支付页面语言

您可以通过设置lang参数自定义页面语言。

目前支持:

日语(ja), 韩语(ko), 英语(en), 繁体中文-台湾(ch-TW), 德语(pt_de), 西班牙语(pt_es), 法语(pt_fr), 意大利语(pt_it), 葡萄牙语-巴西(pt_Br), 葡萄牙-葡萄牙(pt_PT), 俄语(ru);

例如:

lang: 'ko'// 将表单语言设置为韩语

如果当前浏览器语言是支持的语言之一,将显示相应的语言;否则,默认显示英语

/* 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. 在客户端完成支付

处理提交事件

当表单的提交事件发生时,通过 MoneyCollect API 确认支付。

完成支付

调用 confirmPayment(),传递 PaymentElement 和一个 return_url 来指示 MoneyCollect 在用户完成支付后应该重定向到的位置。对于需要身份验证的支付,MoneyCollect 会显示一个用于 3D Secure 认证的模态框或根据支付方法将客户重定向到认证页面。客户完成认证过程后,将被重定向到 return_url。

5. 处理支付后事件

使用 Webhook

当传递了 notifyUrl 参数时,MoneyCollect 将使用该 URL 异步通知交易结果。

有关管理 Webhook、处理退款以及在支付后处理响应的详细指导,请参阅以下文档。

6. 其他测试资源

您可以使用几个测试卡来确保您的集成已准备好投入生产。

这些卡可以与任何 CVC, postal code, 以及 future expiration date一起使用。

号码品牌描述

4242 4242 4242 4242

Visa

成功 并立即处理支付。

3566 0020 2036 0505

JCB

成功 并立即处理支付。

6011 1111 1111 1117

Discover

成功 并立即处理支付。

3782 8224 6310 0052

American Express

成功 并立即处理支付。

5555 5555 5555 4444

Mastercard

成功 并立即处理支付。

4000 0000 0000 0077

Visa

始终失败,拒绝代码为declined

4000002500003155

Visa

此卡在所有交易中都需要 3D 认证

Last updated