【问题标题】:How to implement Authorize.NET Hosted Payments iFrame & Laravel如何实现 Authorize.NET Hosted Payments iFrame & Laravel
【发布时间】:2017-09-27 18:39:40
【问题描述】:

我发现 Authorize.NET 提供的官方文档和 github 示例是一堆你不需要的东西,非常混乱。这篇文章是对过去几个小时工作的总结,希望对其他人有所帮助。

本指南假设您不想要收据页面,并且您希望在成功付款后自动将用户向前移动。

【问题讨论】:

    标签: php laravel cross-domain authorize.net


    【解决方案1】:

    网站后端是 Laravel (PHP),但这里几乎没有特定于 Laravel 的内容。

    首先要做的是添加 Authorize.NET SDK:

    composer require authorizenet/authorizenet
    

    然后我设置了一个接受订单的托管支付存储库,但您可以随意执行此操作,这会将托管表单令牌返回到准备传递给视图的控制器。

    <?php
    
    namespace ShopApp\Repositories;
    use ShopApp\Models\Order;
    use ShopApp\Repositories\Contracts\hostedPaymentRepositoryContract;
    use Illuminate\Support\Facades\Config;
    use net\authorize\api\contract\v1\MerchantAuthenticationType;
    use net\authorize\api\contract\v1\TransactionRequestType;
    use net\authorize\api\controller\GetHostedPaymentPageController;
    use net\authorize\api\contract\v1\GetHostedPaymentPageRequest;
    use net\authorize\api\contract\v1\SettingType;
    use net\authorize\api\constants\ANetEnvironment;
    use net\authorize\api\contract\v1\CustomerAddressType;
    use ShopApp\Models\Address;
    
    /**
     * Class hostedPaymentRepository
     * @package ShopApp\Repositories
     * @todo - Implement methods to talk to Authorize.NET and show form.
     */
    
    class hostedPaymentRepository implements hostedPaymentRepositoryContract
    {
    
        public $response; //what did we get back?
        public $paymentFormToken;
    
        public function getHostedFormToken(Order $order){
    
            $payment_amount = null;
    
            foreach($order->items as $order_item){
    
                $payment_amount += $order_item->price;
    
            }
    
            $billing_address = Address::findOrFail($order->billing_address_id);
    
            // Common setup for API credentials
            $merchantAuthentication = new MerchantAuthenticationType();
            $merchantAuthentication->setName(Config::get('yoursite.payment_providers.authorize_dot_net.MERCHANT_LOGIN_ID'));
            $merchantAuthentication->setTransactionKey(Config::get('yoursite.payment_providers.authorize_dot_net.MERCHANT_TRANSACTION_KEY'));
    
            //create a transaction
            $transactionRequestType = new TransactionRequestType();
            $transactionRequestType->setTransactionType("authCaptureTransaction");
            $transactionRequestType->setAmount($payment_amount);
    
            // Create the Bill To info
            $billto = new CustomerAddressType();
            $billto->setFirstName($order->billing_first_name);
            $billto->setLastName($order->billing_last_name);
            $billto->setAddress($billing_address->address_1);
            $billto->setCity($billing_address->city);
            $billto->setState($billing_address->state);
            $billto->setZip($billing_address->zip);
            $billto->setCountry($billing_address->country);
    
            if(!is_null($order->phone)){
    
                $billto->setPhoneNumber($order->phone);
    
            }
    
            //@todo - implement user stuff and get email
            //$billto->setEmail("changethis@later.com");
    
            $transactionRequestType->setBillTo($billto);
    
            // Set Hosted Form options
            $setting1 = new SettingType();
            $setting1->setSettingName("hostedPaymentButtonOptions");
            $setting1->setSettingValue("{\"text\": \"Pay Now\"}");
    
            $setting2 = new SettingType();
            $setting2->setSettingName("hostedPaymentOrderOptions");
            $setting2->setSettingValue("{\"show\": false}");
    
            $setting3 = new SettingType();
            $setting3->setSettingName("hostedPaymentReturnOptions");
            $setting3->setSettingValue("{\"showReceipt\" : false }");
    
            $setting4 = new SettingType();
            $setting4->setSettingName("hostedPaymentIFrameCommunicatorUrl");
            $setting4->setSettingValue("{\"url\": \"https://yoursite.local/checkout/payment/response\"}");
    
    
            // Build transaction request
            $request = new GetHostedPaymentPageRequest();
            $request->setMerchantAuthentication($merchantAuthentication);
            $request->setTransactionRequest($transactionRequestType);
    
            $request->addToHostedPaymentSettings($setting1);
            $request->addToHostedPaymentSettings($setting2);
            $request->addToHostedPaymentSettings($setting3);
            $request->addToHostedPaymentSettings($setting4);
    
            //execute request
            $controller = new GetHostedPaymentPageController($request);
            $response = $controller->executeWithApiResponse(ANetEnvironment::SANDBOX);
    
            if (($response == null) && ($response->getMessages()->getResultCode() != "Ok") )
            {
                return false;
            }
    
            return $response->getToken();
        }
    
    }
    

    请注意,我已将 showReceipt 设置为 false,并提供了另一个名为 hostsPaymentIFrameCommunicatorUrl 的设置。不要忘记hostedPaymentIFrameCommunicatorUrl,否则无论将showReceipt设置为false,您都将获得收据页面。

    hostedPaymentIFrameCommunicatorUrl 必须与您的支付页面在同一个域中,并且在同一个端口上。

    然后在您的视图中,您需要添加 iFrame(我的只是位于页面中,我没有打扰灯箱:

    <div id="iframe_holder" class="center-block" style="width:90%;max-width: 1000px" data-mediator="payment-form-loader">
        <iframe id="load_payment" class="embed-responsive-item" name="load_payment" width="750" height="900" frameborder="0" scrolling="no">
        </iframe>
    
        <form id="send_hptoken" action="https://test.authorize.net/payment/payment" method="post" target="load_payment">
            <input type="hidden" name="token" value="{{ $hosted_payment_form_token }}" />
        </form>
    
    </div>
    

    在同一个视图中,您至少需要加载以下 javascript(我使用 jQuery,并且只有一半实现了 transactResponse 方法,但您明白了):

    $(document).ready(function(){
    
        window.CommunicationHandler = {};
    
        function parseQueryString(str) {
            var vars = [];
            var arr = str.split('&');
            var pair;
            for (var i = 0; i < arr.length; i++) {
                pair = arr[i].split('=');
                vars[pair[0]] = unescape(pair[1]);
            }
            return vars;
        }
    
        window.CommunicationHandler.onReceiveCommunication = function (argument) {
    
            console.log('communication handler enter');
    
            var params = parseQueryString(argument.qstr)
    
            switch(params['action']){
                case "resizeWindow"     :
    
                    console.log('resize'); break;
    
                case "successfulSave"   :
    
                    console.log('save'); break;
    
                case "cancel"           :
    
                    console.log('cancel'); break;
    
                case "transactResponse" :
    
                    sessionStorage.removeItem("HPTokenTime");
    
                    console.log('transaction complete');
    
                    var transResponse = JSON.parse(params['response']);
    
                    window.location.href = '/checkout/complete';
    
            }
        }
    
        //send the token
        $('#send_hptoken').submit();
    
    
    });
    

    上面的代码添加了一个函数来处理从 iFrame Communicator 返回的消息(下一步),并提交支付表单令牌以获取并加载实际的支付表单。

    接下来我们需要设置一个“iframe 通信器”这基本上只是 Authorize.NET 绕过same-domain origin policy 的一种方式

    为此,请创建一个新路由和一个仅返回简单 HTML 页面(或刀片模板,但除了脚本之外不应有其他内容)的视图。

    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>IFrame Communicator</title>
        <script type="text/javascript">
    
        function callParentFunction(str) {
    
            if (str && str.length > 0 && window.parent.parent
                && window.parent.parent.CommunicationHandler && window.parent.parent.CommunicationHandler.onReceiveCommunication) {
    
                var referrer = document.referrer;
                window.parent.parent.CommunicationHandler.onReceiveCommunication({qstr : str , parent : referrer});
    
            }
    
        }
    
        function receiveMessage(event) {
    
            if (event && event.data) {
                callParentFunction(event.data);
            }
    
        }
    
        if (window.addEventListener) {
    
            window.addEventListener("message", receiveMessage, false);
    
        } else if (window.attachEvent) {
    
            window.attachEvent("onmessage", receiveMessage);
    
        }
    
        if (window.location.hash && window.location.hash.length > 1) {
    
            callParentFunction(window.location.hash.substring(1));
    
        }
    
    </script>
    </head>
    <body></body>
    </html>
    

    这里的关键部分是window.parent.parent

    Authorize.NET 获取您在开始时提供的 hostsPaymentIFrameCommunicatorUrl 并将其嵌入到另一个 iFrame 中,即他们自己的支付 iFrame 中。这就是为什么它是 window.parent.parent。

    您的 hostsPaymentIFrameCommunicatorUrl 脚本然后可以将付款响应传递到您的付款页面,然后您可以编辑上述函数以执行您喜欢的操作。

    希望对某人有所帮助。

    Authorize.NET 严重缺乏示例,他们的文档充其量只是轻量级的。让您筛选大量不需要的代码的“包罗万象”示例只是懒惰。

    虽然他们的 API 文档还不错,但他们只需要体面的示例...

    【讨论】:

    • 您的最终评论非常正确。这些例子试图在一个例子中解释太多的概念。他们只需要将其分解为几个已定义的任务示例。不管怎样,你的回答很棒!我遇到了同样的问题。
    • 非常感谢您提供这个完整的工作示例!让它以简单的方式在我的应用程序上工作。清晰的代码和良好的解释。
    • 他们的文档很烂。几天来,我一直在筛选文档,试图弄清楚如何做一些最基本的事情。例如,使用 PHP SDK,如何将 profile/customerProfileId 添加到 getHostedPaymentPageRequest?文档中的方法显示:* profile, ** customerProfileId - 所以你会认为它是$profileType = new AnetAPI\ProfileType() ; - 但不,ProfileType 不存在。
    • 只收到resize 事件是否正常?我觉得我应该得到成功/失败的方法,但我得到的只是resize。除了,当我单击 Continue 按钮时,我得到一个 action=cancel 事件。 (???)
    猜你喜欢
    • 2021-11-20
    • 1970-01-01
    • 2012-07-29
    • 1970-01-01
    • 2013-11-18
    • 2021-07-15
    • 2020-02-12
    • 2011-01-19
    • 2015-07-19
    相关资源
    最近更新 更多