65.9K
CodeProject 正在变化。 阅读更多。
Home

Bitcoin jQuery 支付小部件

starIconstarIconstarIconstarIconstarIcon

5.00/5 (11投票s)

2017年10月16日

CPOL

5分钟阅读

viewsIcon

25249

downloadIcon

459

编码 jQuery 小部件以接受比特币支付

引言

本文介绍了一个使用 Bitcoin 进行支付的 jQuery 小部件。

使用 Bitcoin 支付

那么,到底需要什么才能用 Bitcoin 支付呢?

与其他传统支付系统不同,Bitcoin 要求每笔交易都需要由存储在付款人钱包中的私钥预先签名。

此私钥不能公开,因此使用 Bitcoin 安全支付的唯一方法是通过商家提供的地址和金额,从付款人的钱包中发起支付。

付款人以 QR 码的形式扫描此信息。扫描信息并启动支付后,付款人应通知小部件开始监控交易。

当小部件从 Bitcoin 网络接收到交易后,支付即被视为成功。

自动更新

Bitcoin 的汇率变化非常频繁,而且目前非常不稳定。

因此,付款地址和金额(以 QR 码形式)应不断自动更新,以反映服务的最新价格。

注意:地址通常会随着金额一起更改,因为这是一种更安全的方法。

小部件要求

总而言之,小部件应能够做到以下几点:

  1. 以 QR 码形式向付款人显示最新的商家地址和金额信息
  2. 每 5 分钟自动更新一次 QR 码。在 QR 码过期前至少 1 分钟,通过计时器通知付款人。
  3. 提供一个按钮供付款人通知支付已提交。点击按钮后开始监控新交易。
  4. 收到交易后,执行必要的回调并通知付款人支付已成功。

使用此小部件

小部件需要 jQuery 和 jquery-qrcode 库。

以下是使用示例

HTML

    <div class="bitcoin-payment-widget" id="widget">
        <div class="border-cover">
            <div class="header-logo"><img src="~/Content/bitcoin.png" /></div>
            <div class="header-label">Bitcoin Payment</div>
            <div class="qr-image-container">
                <div class="qr-image" data-role="qr-image"></div>
            </div>
            <div class="update-label" data-role="update-label"></div>
            <div class="check-payment-button-container">
                <button class="check-payment-button" 
                data-role="check-payment-button"></button>
            </div>
        </div>
    </div>

JavaScript

        $(document).ready(function () {
            $("#widget").bitcoinpayment({
                getNewLinkUrl: "/Payment/GetNewLink",			
                getPaymentStatusUrl: "/Payment/GetPaymentStatus",
                postPaymentUrl: "/Payment/PostPayment",
                postPaymentCallback: function (result) {
                    alert("Payment received at " + result.paidAt + ", 
                           transaction id: " + result.transactionId);
                }
            });
        })

此小部件的前提条件是一个 REST 服务,该服务应与 Bitcoin(或上层)网络通信,并能够生成新的地址/金额对并检查其相应的交易状态。

以下是参数对象的描述

  • getNewLinkUrl - 返回 Bitcoin link 和与之对应的唯一 paymentRequestId 的 REST 服务方法的 URL。Bitcoin 链接应为 URI 格式(请参阅 Bitcoin URI),并且应包含地址和金额。
  • getPaymentStatusUrl - 接受 paymentRequestId 字符串作为参数并返回 paymentSuccessful 布尔值的 REST 服务方法的 URL。此服务方法应请求 Bitcoin(或上层)网络并检查是否存在与特定 paymentRequestId 相关的任何交易。如果为 true,则该方法应返回 paymentSuccessfultrue。可选地,还可以返回 paidAttransactionId
  • postPaymentUrl - 成功支付后回传的 REST 服务的 URL。应将 paymentRequestId 作为参数。
  • postPaymentCallback - 成功支付后触发的回调。

以下是可能的服务器端实现(使用 C# 和 ASP.NET)

       [HttpPost]
        public ActionResult GetNewLink()
        {
            var paymentInfo = new PaymentInfo()
            {
                Amount = 0.25M,
                Currency = "USD"
            };

            var paymentRequest = BitcoinNetworkService.GenerateNewPaymentRequest(paymentInfo);

            return Content("{ \"link\": \"" + 
            paymentRequest.BitcoinUri + "\", \"paymentRequestId\": \"" + 
            paymentRequest.PaymentRequestId + "\" }");
        }

        [HttpPost]
        public ActionResult GetPaymentStatus(string paymentRequestId)
        {
            var paymentResult = BitcoinNetworkService.GetPaymentStatus(paymentRequestId);

            return Content("{ \"paymentSuccessful\": " + 
            paymentResult.IsSuccessful.ToString().ToLowerInvariant() +
                           ", \"paidAt\": \"" + 
                           paymentResult.PaidAt + "\", \"transactionId\": \"" + 
                           paymentResult.TransactionId + "\" }");
        }

        [HttpPost]
        public ActionResult PostPayment(string paymentRequestId)
        {
            // Posting payments
            return new EmptyResult();
        }

演示项目

演示项目使用 Coinbase 实现服务器逻辑。要运行演示,请在 Coinbase 中创建一个 Merchant 帐户,并在 Web.Config 文件中指定凭据(CoinbaseApiKeyCoinBaseApiSecret)。

小部件实现细节

让我们从初始化开始观察代码。

    function init($container) {
        $qrImage = $container.find("[data-role='qr-image']");
        $checkPaymentButton = $container.find("[data-role='check-payment-button']");
        $updateLabel = $container.find("[data-role='update-label']");

        $checkPaymentButton.text("Check Payment");
        $updateLabel.text("Pay QR in your wallet");

        $checkPaymentButton.click(function () {
            if (isMonitoringPayments) {
                $updateLabel.text("Pay QR in your wallet");
                $checkPaymentButton.text("Check Payment");
                stopMonitoringPayments();
                startAutoUpdater();
            }
            else {
                startMonitoringPayments();
                stopAutoUpdater();
                $checkPaymentButton.text("Cancel");
                $updateLabel.text("Checking payment...");
            }
        })

        startAutoUpdater();
    }

在初始化时,我们应该做几件事:初始化变量、启动自动更新器并根据“检查支付”按钮的当前状态(可以是“检查支付”或“取消检查支付”)来挂钩其逻辑。

首先,参数 $qrImage$checkPaymentButton$updateLabel 使用具有特定 data-role 属性的 HTML 元素进行初始化。使用这些属性可以使 HTML 更加健壮,并在需要时使用不同的标记来表示元素。

然后,为 $checkPaymentButton 分配一个 click 事件,该事件有两个条件。如果此按钮在小部件未监控支付时被点击,则表示用户点击了检查支付按钮,小部件应开始监控支付并停止 QR 码的自动更新。否则,表示用户点击了取消按钮,在这种情况下,与之前的行为相反,小部件应中断支付监控并继续 QR 码的自动更新。相应地更新 $updateLabel$checkPaymentButton

在方法结束时,执行 startAutoUpdater 来启动自动更新器并执行 QR 码的初始更新。

现在让我们更详细地看看主要函数。

    function checkForUpdate() {
        var minimumNotificationTime = 60000;
        var currentTime = Date.now();
        if (!timeToNextUpdate || timeToNextUpdate.getTime() < currentTime) {
            timeToNextUpdate = new Date(currentTime + updateIntervalInMinutes * 60000)
            $qrImage.empty();
            $.post(options.getNewLinkUrl)
            .done(function (r) {
                var result = JSON.parse(r);
                console.log("Received link: " + result.link);
                paymentRequestId = result.paymentRequestId;
                $qrImage.empty();
                $qrImage.qrcode({
                    text: result.link,
                    width: $qrImage.width(),
                    height: $qrImage.height()
                });
                $updateLabel.text("Pay QR in your wallet");
            })
            .fail(function () {
                console.error("Service " + options.getNewLinkUrl + " is not accessible.");
            });
        }
        else if (timeToNextUpdate.getTime() - currentTime <= minimumNotificationTime) {
            $updateLabel.text("Time before update: " + 
                               fmtMMSS(timeToNextUpdate.getTime() - currentTime));
        }
    }

此函数在自动更新器循环的作用域内执行。其主要职责是使用最新的金额和支付地址更新 QR 码。

timeToNextUpdate 是每次生成 QR 码时生成的时间。它在此方法中使用 currentTimeupdateIntervalInMinutes 的总和乘以 60000 来分配,因为所有这些值都以毫秒为单位。当 timeToNextUpdate 未设置(初始状态)或小于 currentTime 时,表示时间已到,需要更新 QR 码。在此更新中,小部件向选项中提供的 getNewLinkUrl 发送 POST 请求,该请求必须返回一个 JSON string,其中包含 paymentRequestIdlink,格式为 Bitcoin URI,包含支付和商家地址的信息。QR 码使用 qrcode 函数更新,该函数是 jquery.qrcode 库的一部分(请参阅 GitHub)。minimumNotificationTime 变量存储了用户何时需要被告知 QR 码即将过期的时间。当该时间到来时,用户将在 $updateLabel 中看到有关 QR 码即将过期的通知。

    function checkPayment() {
        $.post(options.getPaymentStatusUrl,
            {
                paymentRequestId: paymentRequestId
            })
        .done(function (r) {
            var result = JSON.parse(r);
            if (result.paymentSuccessful) {
                console.log("Payment received")
                stopMonitoringPayments();
                stopAutoUpdater();

                $updateLabel.text("Payment Succeeded!");
                $checkPaymentButton.attr("disabled", "disabled")

                $.post(options.postPaymentUrl, {
                    paymentRequestId: paymentRequestId
                })
                .done(function () {
                    if (options.postPaymentCallback) {
                        options.postPaymentCallback(result);
                    }
                });
            }
        }).fail(function () {
            console.error("Service " + options.getPaymentStatusUrl + " is not accessible.");
        });
    }

此函数的主要目的是检查服务器是否已收到付款。

系统使用 paymentRequestIdgetPaymentStatusUrl 发送请求,并收到包含支付状态的 JSON 响应。当支付成功后,系统会停止进程并调用回调。首先,系统停止监控和自动更新。$updateLabel 会更新以通知付款人交易已成功。然后,$checkPaymentButton 会被禁用,以防止用户进行任何进一步操作,因为小部件上的所有操作都已停止。最后,在 postPaymentUrl 上执行回传,并在 postPaymentCallback 上调用以分别通知服务器和客户端成功的结果。

关注点

此小部件未涵盖用户支付金额大于或小于所需金额的边缘情况。但是,如果服务器端实现了适当的功能,则可以轻松添加。

© . All rights reserved.