Giter Site home page Giter Site logo

silverstripe-omnipay's Introduction

SilverStripe Payments via Omnipay

CI Code Coverage Latest Stable Version Total Downloads Latest Unstable Version

Live chat: ![Gitter](https://badges.gitter.im/Join Chat.svg)

The aim of this module is to make it easy for developers to add online payments to their SilverStripe application. In a nutshell, it wraps the PHP Omnipay payments library and provides some additional functionality. To understand more about omnipay, see: https://github.com/thephpleague/omnipay

Requirements

Features

  • Gateway configuration via YAML config.
  • Payment / transaction model handling.
  • Detailed + structured logging in the database.
  • Provide visitors with one, or many gateways to choose from.
  • Provides form fields, which can change per-gateway.
  • Caters for different types of gateways: on-site capturing, off-site capturing, and manual payment.
  • Wraps the Omnipay php library.
  • Multiple currencies.

Compatible Payment Gateways

There are many gateways available, which you can install separately.

Searching packagist is useful: https://packagist.org/search/?q=omnipay

It is not too difficult to write your own gateway integration either, if needed.

Installation

Composer is currently the only supported way to set up this module:

composer require silverstripe/silverstripe-omnipay ^3@dev

You will also need to pull in your payment adapter of choice. Have a look at http://omnipay.thephpleague.com/gateways/official/ where the second column is the package name.

For example, if your site uses PayPal you would also need to run:

composer require omnipay/paypal

There's also short guide how to enable manual payments or PayPal Express available.

Configuration

Silverstripe Omnipay offers a lot of configuration options. A full list can be found in our dedicated configuration documentation.

Gateway naming conventions

The way gateways are named is dictated by the Omnipay module. Since there might be different gateways in one Omnipay-Payment-Driver, we need a way to address these via different names.

The rules are pretty simple: Class names beginning with a namespace marker (\) are left intact. Non-namespaced classes are expected to be in the \Omnipay namespace. In non-namespaced classes, underscores or slashes (\) are used to denote a specific gateway instance.

Examples:

  • \Custom\Gateway\Custom\Gateway
  • \Custom_Gateway\Custom_Gateway
  • Stripe\Omnipay\Stripe\Gateway
  • PayPal\Express\Omnipay\PayPal\ExpressGateway
  • PayPal_Express\Omnipay\PayPal\ExpressGateway

And another example: Omnipay PayPal comes with three different gateway implementations: ExpressGateway, ProGateway and RestGateway. The gateway names for these gateways would be: PayPal_Express, PayPal_Pro and PayPal_Rest.

Please follow the rules above to choose the correct gateway name in your configuration files.

Throughout the documentation and examples of this module, you'll find the syntax with underscores. It's easier to read and less error-prone (escaping) than the syntax with namespace markers (\).

Usage

We have produced a comprehensive getting started guide in our documentation pages

The Payment Services and Service Factory

There are currently five payment services available, which map to methods exposed by Omnipay. Which one you can use in practice depends on the capabilities of the individual gateway. Some gateways will support all services, while some support only a few (eg. the "Manual" gateway doesn't support "purchase").

The services are:

  • PurchaseService : Directly purchase/capture an amount.
  • AuthorizeService: Authorize an amount.
  • CaptureService: Capture a previously authorized amount.
  • RefundService: Refund a previously captured amount.
  • VoidService: Void/Cancel an authorized payment.

Each of these services implements an initiate and a complete method. The initiate method is always required and initiates a service. Depending on how the gateway handles requests, you might also need the complete method.

This is the case with offsite payment forms, where initiate will redirect the user to the payment form and once he returns from the offsite form, complete will be called to finalize the payment.

Another (less common) case is, when the payment provider uses asynchronous notifications to confirm changes to payments.

While you can instantiate the services explicitly, the recommended approach is to use the ServiceFactory. The service factory allows easy customization of which classes should be instantiated for which intent. The service-factory can also automatically return an AuthorizeService or PurchaseService, depending on what was configured for the chosen Gateway.

The following constants are available to instantiate Services:

  • INTENT_PURCHASE requests a purchase service.
  • INTENT_AUTHORIZE requests an authorize service.
  • INTENT_CAPTURE requests a capture service.
  • INTENT_REFUND requests a refund service.
  • INTENT_VOID requests a void service.
  • INTENT_PAYMENT returns authorize- or purchase-service, depending on selected Gateway.

In code:

$payment = Payment::create()->init("PxPayGateway", 100, "NZD");

// The service will be a `PurchaseService`
$service = ServiceFactory::create()->getService($payment, ServiceFactory::INTENT_PURCHASE);

// Initiate the payment
$response = $service->initiate($data);

Passing correct data to the purchase function

The omnipay library has a defined set of parameters that need to be passed in. Here is a list of parameters that you should map your data to:

transactionId
firstName
lastName
email
company
billingAddress1
billingAddress2
billingCity
billingPostcode
billingState
billingCountry
billingPhone
shippingAddress1
shippingAddress2
shippingCity
shippingPostcode
shippingState
shippingCountry
shippingPhone

Note: transactionId can be a reference that identifies the thing you are paying for, such as an order reference id. It usually shows up on bank statements for reconciliation purposes, but ultimately depends how the gateway uses it.

Security

When customizing the payment flow (e.g. subclassing PaymentForm or OrderProcessor), please take care to only pass whitelisted user input to PurchaseService and the underlying omnipay gateways. The easiest way to ensure no arbitrary data can be injected is by using Form->getData() rather than acessing $_REQUEST directly, since this will only return you data for fields originally defined in the form.

Debugging payments

Please read the logging documentation on how to set up logging.

Renaming gateways and translation

You can change the front-end visible name of a gateway using the translation system. The gateway name must match what you entered in the allowed_gateways YAML config.

For example, inside mysite/lang/en.yml:

en:
  Gateway:
    Paystation_Hosted: "Credit Card"
    PayPal_Express: "PayPal"

This approach can also be used to provide different translations. For further information about module translations, please read docs/en/Translating.md

Further Documentation

https://github.com/silverstripe/silverstripe-omnipay/blob/master/docs/en/index.md

silverstripe-omnipay's People

Contributors

antonythorpe avatar bummzack avatar catharsisjelly avatar chillu avatar cjsewell avatar dhensby avatar digitall-it avatar fb3rasp avatar hailwood avatar hchokshi avatar jedateach avatar joshkosmala avatar kinglozzer avatar laminbarrow avatar madmatt avatar mikeyc7m avatar mlewis-everley avatar nspyke avatar spekulatius avatar stephenhonor avatar sunnysideup avatar thebnl avatar tractorcow avatar uniun avatar wernerkrauss avatar wilr avatar zanderwar avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

silverstripe-omnipay's Issues

Implement AuthorizeCaptureService (delayed purchase)

The module should make provision for either making payment via purchase or authorise & capture.
All gateways provide purchase, but some allow authorize & capture.

The payment module could always go for authorise, if available.

Token credit cards

Provide some kind of datamodel for storing credit card tokens.

Security should be heavily considered here, because this could be a way for cards to be accessed.

Should tokens expire and be removed? Or would this happen at the gateway end?

Payment state machine

Figuring out what actions should be allowed, according to a state machine. There are a few I'm not sure about.

Status\Action purchase() authorize() capture() refund() void()
Created
Authorized
Captured
Refunded
Void

Restricting the status of payments can help ensure things aren't changed accidentally, or when they shouldn't be.

A possible implementation:

    private $state_machine = array(
        'Created' => array('Captured','Authorized'),
        'Authorized' => array('Captured'),
        'Captured' => array('Refunded','Void')
        'Refunded' => null,
        'Void' => null
    );
    /**
     * Only allows updating the status, according to the state machine
     */
    public function setStatus($status){
        if($status instanceof Enum){
            $this->dbObject("Status")->setValue($status);
        } elseif($this->statusCanChangeTo($status)) {
            $this->dbObject("Status")->setValue($status);
        }
    }

Unit tests

We don't need to test the gateway handling...that is the job of omnipay.

Use either dummy payment or manual payment for tests. Not sure how to test isRedirect == true functionality.

Things that still need to be tested for 1.0 release:

  • configuring gateways
  • creating a payment
  • state machine restrictions
  • onsite and offsite gateway interactions (using mock responses)
  • dummy / manual gateways
  • Payable extension
  • logging: produce every type of PaymentMessage
  • on-site missing credit card details
  • on-site invalid credit card details (expired, lhun fail, etc)
  • 0 or negative amounts

..as these are checked off, we may find that the system design may change a little

The transaction currency specified must be the same as previously specified.

Hi Jeremy

I have setup paypal via omnipay.

It works with USD - but if I change it to NZD I get the following message:

"The transaction currency specified must be the same as previously specified."

I am just lodging this here, because it appears that the system is sending mixed messages to paypal. I am not sure yet, just flagging this as a potential issue.

This is the code I am using:

    function PaymentForm(){
        if($this->showPaymentForm) {
            $gateway = "PayPal_Express";
            $factory = new GatewayFieldsFactory($gateway);
            $fields = $factory->getFields();
            $actions = new FieldList(new FormAction("pay", "Pay Now"));
            return new Form($this, "PaymentForm", $fields, $actions);
        }
    }

    function pay($form, $data) {
        $isTestMode = Director::IsLive() ? false : true;
        if($amount = $myPage->AmountToPay() && $currency = $myPage->CurrencyToPay()) {
            $payment = Payment::create();
            $payment->init($gateway = "PayPal_Express", $amount, $currency);
            $paymentService = PurchaseService::create($payment);
            $paymentService->setReturnUrl($this->Link('completepayment'));
            $paymentService->setCancelUrl($this->Link('cancelpayment'));  
            $paymentService->oGateway()->setTestMode($isTestMode);
            $this->result->Payments()->add($payment);
            $purchase = $paymentService->purchase();
            $purchase->redirect();
        }
    }


    function completepayment($request){
        return $this->redirect($this->Link());
    }

    function cancelpayment($request){
        return $this->redirect($this->Link());
    }

I tried in both test and live mode. Same results: USD works, NZD fails.

Payment Reports

There are a number of things that could be reported on:

  • Comparison of gateways used
  • Successful vs failed
    • Step(s) where failure occurred
  • Currencies
  • Plot graph of ammounts

Allow administrators to complete payment

Administrators sometimes need to make/complete a payment. The customer may give credit card details over the phone, or send through some kind of manual payment, and this needs to be recorded/updated by an administrator of the application.

An interface will need to be created to allow completing (possibly also creating) payments via any type of gateway. This interface should be accessible from the CMS.

Logging, transactions, and transitions

Logging is really important for seeing a history of what has happened in a system. We want to know everything that has happened in regards to a payment over time.

  • Every payment transaction should be time-stamped.
  • Message sources could include: system, gateway, user, administrator
  • Failures are acceptable outcomes.
  • Errors are problems with the system / gateway.
  • Logging could optionally also occur in a log file.
  • Some things will be visible to the user, others won't. eg errors will probably be a generic message for the user
  • Messages should be translatable

Do we want to log requests AND responses for gateway interactions? or should these be combined into one object?

I'm attempting to come up with some kind of exhaustive list:

Category Log message Notes Action good/bad
Initiation Payment initiated The payment has been created, but nothing happend to it yet 👍
GatewayResponse Purchase request initiated either onsite or offsite authorized or captured 👍
GatewayResponse Purchase completed via gateway offsite only captured 👍
GatewayResponse Credit card authorised for payment authorized 👍
GatewayResponse Payment captured captured 👍
GatewayResponse Credit card declined expired, invalid, fraud check fail 👎
GatewayResponse Required data is missing or invalid eg: amount is missing, or currency isn’t supported 👎
GatewayResponse Gateway authorisation credentials are incorrect 👎
GatewayResponse Successful refund refunded 👍
GatewayResponse Refund failed Nothing went wrong with the system/gateway, but perhaps the credit card is now invalid. failure 👎
GatewayRequest Gateway can't be contacted/reached 👎
GatewayRequest Gateway response can't be read/parsed 👎
GatewayResponse Manual payment initiated Intent to pay by manual process provided. This is a special type of gateway? authorized 👍
Gateway/SystemResponse Manual payment completed Administrator marks the manual payment as a success completed 👍
Gateway/SystemResponse Manual payment failed cheque bounced. wire transfer didn't happen void 👎
HumanAction User cancelled payment this may called from external gateway site, and update local app. Or it may only happen locally, and then possibly update the external gateway record. void
HumanAction Administrator cancelled payment void
System Payment timed out The system automatically voided an authorised or created payment because it got too old. void
Comment Note/Comment by administrator has_one user
Comment Comment by user has_one user

more...?

This list of scenarios helps to understand where things can be logged:

https://github.com/burnbright/silverstripe-omnipay/blob/master/docs/en/payment-scenarios.md

Payment handling between requests

These are the states I gather a payment can have: Created, Authorised, Captured, Refunded, or Void.

I'm guessing that if there are redirects occurring between steps, then you'll need to store gateway response data to be retrieved again.

One important question is where to store this data. It could go in the database model, or in the session.
I'm guessing some data you may want to store indefinitely for any later actions, other data you may want to log for debugging or viewing history, and some you may want to throw away asap because it is sensitive.

@adrianmacneil - do you have any recommendations about how to store data between requests?
Would you see any issue with authorising a payment at one step in your application, and then capturing during another step (different http requests)?

Notify request handling needs to be resolved

Gateway servers doing a notify callback will run in a new session.
Currently it will execute whatever it can as if it were the customer. That means redirect urls will potentially be called.

This module should introduce some way to tell if a completePurchase request has come from an external gateway server, and act accordingly.

This issue is not documented well

PaymentController should adjust, based on available gateways

If there is only one gateway, then there is no need to select a gateway.

If a gateway is off-site, then there isn't much need to display a step where the user clicks 'make payment'. If we end up skipping the index view, then it should only be done if there are no previously failed payments. That is because a failed off-site payment could return to the merchant site, and then automatically redirect back to the external gateway....which may be a confusing user experience.

is this the way to do the Paypal Test Payment and how do I know it is a test and not a real payment

Hi Jeremy

I have the following code:

    function PaymentForm(){
        if($this->showPaymentForm) {
            $gateway = "PayPal_Express";
            $factory = new GatewayFieldsFactory($gateway);
            $fields = $factory->getFields();
            $actions = new FieldList(new FormAction("pay", "Pay Now"));
            return new Form($this, "PaymentForm", $fields, $actions);
        }
    }

    function pay($form, $data) {
        $isTestMode = Director::IsLive() ? false : true;
        if($amount = $this->result->AmountToPay()) {
            $currency = $this->result->CurrencyToPay();
            $payment = Payment::create()
                ->init($gateway = "PayPal_Express", $amount, $currency)
                ->setReturnUrl($this->Link('completepayment'))
                ->setCancelUrl($this->Link('cancelpayment'));  //$form->getData()
            Session::set("OmniPayPaymentID", $payment->ID);
          $payment->oGateway()->setTestMode($isTestMode);
            $purchase = $payment->purchase();
            $purchase->redirect();
        }
    }


    function completepayment($request){
        $id = Session::get("OmniPayPaymentID");
        Session::clear("OmniPayPaymentID");
        $payment = Payment::get()->byID($id);
        $payment->Status = "Authorized";
        $payment->write();
        return $this->redirect($this->Link());  }

    function cancelpayment($request){
        $id = Session::get("OmniPayPaymentID");
        Session::clear("OmniPayPaymentID");
        $payment = Payment::get()->byID($id);
        $payment->Status = "Void";
        $payment->write();
        return $this->redirect($this->Link());
    }

and in yml:

---
Name: payment

---
Payment:
  has_one:
    QuestionnaireResult: QuestionnaireResult
  allowed_gateways:
    - 'PayPal_Express'
    - 'Manual'
  parameters:
    PayPal_Express:
      username: 'pay_api1.sunnysideup.co.nz'
      password: 'real password'
      signature: 'real signature'

---
Only:
    environment: 'live'

---
Payment:
    parameters:
        PayPal_Express:
            username: 'liveexample.test'
            password: 'livepassawe23'
            signature: 'laivfe23235235'

This seems to work ok, except that I dont think it is in test mode...

I am not sure where I can find more information about this or how to fix his.

Ideally I could also write $payment->IsTestMode(true) rather than the construction above...

ALSO - the way I process the payments (cancel and success) is DEFINITELY wrong (you can just browse to the URLs to make a payment!) ... How do you envisage this to work?

Cheers

Nicolaas

Visual diagrams of payment process

@adrianmacneil - just wondering if you have any visual diagrams of either the omnipay classes, or a flow diagram of how the omnipay fits in within an application / the gateways it is talking to?

Administrate/clean incomplete/abandoned payments. Avoid over-payments.

Some payments fail, so they show up as "Authorized" or "Created". Payments should not remain in these statuses. Sometimes these payments may have actually succeeded in the gateway.

There are a few ways to clean these up:

  • Administrators should be able to run the 'completePayment' function so to do a lookup that either voids the payment, or completes it.
  • Administrators should be able to complete/void manual payments. Replaces #51
  • Automatic task can tidy up payments that are older than X period.
  • When a payment is re-attempted on the same Payable object, it could check if incomplete payments were successful.

What happens if a particular gateway becomes unavailable?

If a gateway is renamed or removed from the system, any transaction functions will fail, probably disastrously.

I think it would be good to allow omnipay payment types to come, and go from the system, yet everything still works. Payments won't be able to be completed.

Improve language/terminology of classes/functions/variables

Because there are multiple aspects that do much the same thing, there is some crossover of terms. This can make debugging and understanding the code quite difficult.

Below are terms to better clarify in code:

Response

  • omnipay Responses
  • GatewayResponse - used to encapsulate the omnipay response info and aid in redirection
  • SS_HTTPResponse - ss native responses

Redirect

  • Gateway redirects - to send the user to an offsite gateway website for entering payment creds
  • Application redirects - after SS omnipay processing. Variables are inconsistent for this, eg: $message->SuccessURL = $data['returnUrl']

Identifiers / References

  • Identifier (on PaymentMessage) - Internally random generated string for uniquely referencing the payment in url.
  • TransactionReference - function on omnipay responses .... ?

Messages

  • PaymentMessage vs
  • Guzzle Message

@chillu - feel free to add your 2c to help make this clearer, or propose changes.

_config/payment.yml should be moved

This file merges with one's real configuration causing Dummy, Manual, PayPal_Express, etc to show up alongside the real options. The only way to get rid of that is to use Config::inst()->remove in _config.php which is messy. This example data is already in the README.md so shouldn't this file be removed?

Use unique references, instead of ID?

'Reference' => 'Varchar'

    /**
     * Generate a random reference that hasn't been used before.
     * @return [type] [description]
     */
    static function generate_unique_reference(){
        $ref = uniqid("",true);
        while(Payment::get()->filter('Reference',$ref)->exists()){
            $ref = uniqid("",true);
        }
        return $ref;
    }

Payable Extension fails as it requires a has_one relationship from the Payment DataObject

Hi,
When I apply the Payable Data Extension to a simple DataObject, like Order, I get a "[User Error] Uncaught Exception: No has_one found on class 'Payment', the has_many relation from 'Order' to 'Payment' requires a has_one on 'Payment'" error back and can not open the EditForm in the CMS of my Dependent Object anymore.

Not sure if you can do anything as it looks more like a core bug as $has_many relationships in DataExtensions may not work.

Simulating on-site payment with an iframe

A trick to simulate an on-site payment form is to display the external payment form inside an iframe.

It would be good to provide either tools, or instructions on how to achieve this.

Payable data mapping to ominpay fields

Omnipay library and gateways define their fields (parameters), and this is non-negotiable.

Need to provide a mechanism to easily map fields from given data / Payable into omnipay parameters.

Exception handling

What should happen on failure? > pass exceptions to developer

This might just need to be documented.

Static return urls don't work when using off-site gateways

If a gateway doesn't support dynamic return urls, then this module currently won't work.

A solution to this is to implement your own PaymentGatewayController to look up a transaction/payment, based on the data that is passed back.

Another option could possibly be to store a payment id in the session, which is then referenced on return. This probably has implications.

Upgrade to omnipay 2+

This will require rewriting some of the way unit tests retrieve mock data. It is currently a hard-coded path, but will need to shift towards retrieving it from the appropriate composer package automatically.

Looking at the ominpay library tests should help here.

This will bring a few improvements, including being able to pass item descriptions to gateways: https://github.com/omnipay/omnipay/releases/tag/v2.0.0

If their github releases page is anything to go by, it looks like there are no further breaking changes, and as such could upgrade straight to omnipay 2.3.x

see also
http://omnipay.thephpleague.com/changelog/

Store IP Addresses in messages

IP addresses are handy to keep a log of, so these should be attached to each message.

There's definitely a way to capture the request IP from SilverStripe to put in omnipay request messages.
Not exactly sure how to get the ip for omnipay responses.

Add ip addresses to payment summary fields.

your-site-url/dev/payment available getways url breaks

I get a [Notice] Undefined variable: factory error
it's on line 67 of /var/www/pdiver/omnipay/code/admin/PaymentDevelopmentAdmin.php

I think it's trying to return a bit of jQuery for the browser to read but the PHP interpreter is finding it first

Make it easy to test apps

It is tricky to write unit tests for apps that use the omnipay module. You currently need to replicate much of the setup used in the omnipay tests.

Currently difficult:

  • Making use of mocking
  • Getting the correct PaymentGatewayController endpoint to call for mimicking off-site redirects & callbacks.

This needs to be documented.

It may be worth making this improvement at the same time as upgrading to omnipay v2 #23

Also, worth documenting is how to mimic another running session, by creating a new TestSession during the test.

Restrict changing payment status

Restricting the status of payments can help ensure things aren't changed accidentally, or when they shouldn't be.

    private $state_machine = array(
        'Created' => array('Captured','Authorized'),
        'Authorized' => array('Captured'),
        'Captured' => array('Refunded','Void')
        'Refunded' => null,
        'Void' => null
    );
    /**
     * Only allows updating the status, according to the state machine
     */
    public function setStatus($status){
        if($status instanceof Enum){
            $this->dbObject("Status")->setValue($status);
        } elseif($this->statusCanChangeTo($status)) {
            $this->dbObject("Status")->setValue($status);
        }
    }

Document how offsite payments update the model

It is not clear how off-site payments continue execution on the site after redirecting back from the external gateway website.

The payment model will be updated automatically, but the application model will probably need to be updated at this point also.

Currently we have the Payment->onCaptured extension to be hooked into by creating a DataExtension for Payment.

thanks to @wilr for pointing this out.

Gateway callback order can break shop

If a gateway calls back to the server before redirecting the user, then the OrderProcessor->placeOrder() function will add the order to a session that is started for the gateway call. This means the user can't see the newly placed order.

One solution could be to migrate the user's cart id to the order history, if that cart id now points to a placed order. This could be triggered somewhere in OrderManipulation, but I wish it could happen before that.
Perhaps omnipay module needs a new extension hook in PaymentGatewayController->index() called onAlreadyComplete.

Thanks to @nimeso for helping to find this issue.

Securing PaymentGatewayController

We don't want anyone to be able to hit the PaymentGatewayController endpoint to potentially mark payments as successful.

There are a few ways that restrictions could be imposed:

  • gateway callback - if the gateway supports CompletePurchase or CompleteAuthorize, then the model can only be updated by those functions.
  • unique token - generate a securitytoken that must be passed back in the return url.
  • server IP address - only allow gateway server to contact the payment controller.
  • client IP address - only allow the ip address that initiated the payment to finish it.
  • require HTTPS for any use of the PaymentController
  • logged in member matches

Implement better request/response logging

The current logging approach is rather limited:

  • Only dumping arrays and objects, rather than raw data.
  • Uses SilverStripe debug.log, which could get cluttered with other things.

Solution: make use of existing guzzle logging tools. Such logging has a great amount of flexibility to log in various formats and to various locations.

It would be good if raw data could be stored in db, if desired. This could pose security issues however.

Here's how we can hook directly into the Guzzle client and log raw requests and responses:

Additional composer requirements

composer require guzzle/log *@stable
composer require guzzle/plugin-log *@stable
composer require monolog/monolog *@stable

Add this to mysite/_config.php:

<?php

use Guzzle\Http\Client;
use Guzzle\Log\MessageFormatter;
use Guzzle\Log\MonologLogAdapter;
use Guzzle\Plugin\Log\LogPlugin;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;

$logger = new Logger('client');
$logger->pushHandler(new StreamHandler(BASE_PATH.'/omnipay.log'));
$logAdapter = new MonologLogAdapter($logger);
$logPlugin = new LogPlugin($logAdapter, MessageFormatter::DEBUG_FORMAT);
$client = new Client();
$client->addSubscriber($logPlugin);

PaymentService::set_http_client($client);

Adding to _config.php probably isn't the best idea. Would be better to set up on some closure callback. The static setter isn't that great either.

Cons to this approach

If a gateway doesn't use guzzle, it will need to do its own logging. I suspect this is rare, but it should be noted at least.

Providing form fields ...or not

Should form fields be provided?
Could provide fields for data that is not already provided, and is requred by gateway. > can omnipay providet this? (requiredParameters on gateway) provide a credit card entry form / page for on-site payments

Multiple gateways per payment

The Payment model has_many GatewayTransactions. These are a way of keeping a log of what communications have been made with gateway. This log will definitely be useful to see the history of payment transactions.

We could potentially allow making a payment via a different gateway, if the first chosen gateway fails.
A payment becomes a successful payment when any appropriate transaction completes successfully.

Currently Payment has a Gateway field, which just stores the gateway that has been used to make payment.
Payment also has a Status field, with a few possible statuses: Created,Authorized,Captured,Refunded,Void
If a payment has been completed via a particular gateway, then it should only be allowed to be void or refunded by that same gateway.

The current behaviour is to allow changing the gateway only when the status is 'created'. Any other status means that the gateway (and amount+currency) is locked in.

Should we definitely allow more than one gateway to be used to attempt to complete a single payment? The alternative is to force multiple payments per order/invoice/thing.
Do we allow starting a transaction by any such payment type, and concluding it any other way?

Fatal Error 'GatewayFactory' not found

Using shop module and omnipay + default test yaml in readme.

The error occurs at the checkout

Fatal error: Class 'Omnipay\Common\GatewayFactory' not found in C:\wamp\www\bike2013\omnipay\code\model\GatewayInfo.php on line 29

Off-site redirect on failure currently goes to success url

As pointed out in silvershop/silvershop-core#230, there is with redirects after a declined payment via offsite gateway.

An assumption has been made that the omnipay gateway will use the 'cancelURL' when a payment fails. Apparently this isn't the case, and this assumption should no longer be made.

I need to introduce a way to redirect to different urls, based on the gateway response. The current approach is limited to only one url, which encoded in a request parameter.

A new approach could be to store the url in the database. Specifically, adding SuccessURL and FailureURL TEXT database fields to the GatewayRequestMessage class. Only the relative url would need to be stored.

Doing this could help with debugging, because we would have an idea of exact urls that would be visited for each payment type.

@wilr do you think this is a good approach?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.