草庐IT

php - 如何实现 Authorize.NET Hosted Payments iFrame & Laravel

coder 2024-04-12 原文

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

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

最佳答案

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

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

composer require authorizenet/authorizenet

然后我设置了一个接受订单的托管支付存储库,但您可以根据自己的喜好执行此操作,这会将托管表单 token 返回给准备传递给 View 的 Controller 。

<?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,并且我提供了另一个名为 hostedPaymentIFrameCommunicatorUrl 的设置。不要忘记 hostedPaymentIFrameCommunicatorUrl,否则无论将 showReceipt 设置为 false,您都将获得收据页面。

hostedPaymentIFrameCommunicatorUrl 必须与您的支付页面位于同一域和同一端口。

然后在您的 View 中您需要添加 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>

在同一 View 中,您至少需要加载以下 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 返回的消息(下一步),并且还提交了支付表单 token 以获取并加载实际的支付表单。

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

为此,创建一个新路由和一个仅返回简单 HTML 页面(或 Blade 模板,但除脚本外不应包含其他内容)的 View 。

<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 获取您在开始时提供的 hostedPaymentIFrameCommunicatorUrl,并将其嵌入到另一个 iFrame 中,在他们自己的支付 iFrame 中。这就是为什么它是 window.parent.parent。

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

希望对某人有所帮助。

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

他们的 API 文档还不错,他们只需要像样的例子...

关于php - 如何实现 Authorize.NET Hosted Payments iFrame & Laravel,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43700622/

有关php - 如何实现 Authorize.NET Hosted Payments iFrame & Laravel的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  4. ruby-on-rails - rails : "missing partial" when calling 'render' in RSpec test - 2

    我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou

  5. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

    我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

  6. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  7. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  8. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  9. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  10. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

随机推荐