Skip to content
This repository was archived by the owner on Mar 30, 2022. It is now read-only.
Italo edited this page Dec 16, 2018 · 4 revisions

Usage

First, ensure you have a Flow Account, and enabled (or disabled) your accepted payment methods. Additionally, a Flow Sandbox Account to test your application before going to production.

Initialization

The most simple way to initialize the package is by using Flow::make(). Here you must supply the environment, your credentials, and optionally your application Logger instance.

<?php

namespace App;

use App\Logger\MyAppLogger;
use DarkGhostHunter\FlowSdk\Flow;

$flow = Flow::make('production', [
        'apiKey'    => '1F90971E-8276-4713-97FF-2BLF5091EE3B',
        'secret'    => 'f8b45f9b8bcdb5702dc86a1b894492303741c405',
    ],
    new MyAppLogger() // This is optional
);

This static method helps with setting up Flow. Alternatively, you can instance it old-school, but you will have to set the Environment, Credentials, Logger, and Adapter manually. See the method source code for more information.

If you don't have a Logger, don't worry, we will cover this later.

For production environments, explicitly write production, otherwise the sandbox environment will be used by default.

Defaults Base URL, Returns & Webhooks

If you want to save some lines of code, you can set the default URLs that all your transactions will use when you create them.

All the URLs can be overridden by every transaction when you make them just declaring the correct attribute.

Return URLs

Return URLs are the URLs where the user is redirected to when the transaction process ends - independently of the resulting transaction status.

There are only two needed by Flow: when the user ends the payment process, and when the credit card registration ends. We can set them using {service}.{url attribute}. Yes, use the same attributes names as Flow API dictates.

<?php

$flow->setReturnUrls([
    'payment.urlReturn'      => 'https://myapp.com/payment/return',
    'card.url_return'        => 'https://subscriptions.myapp.com/index.php?site=card-return',
]);

$payment = $flow->payment()->make([
    // ...
]);

echo $payment->urlReturn; // https://myapp.com/flow/payment/return

While it's recommended to use your site URL, you can also use another subdomain or domain. You will need to have the same credentials there, though.

Webhooks URLs

Callbacks (correctly called Webhooks), by the other hand, are asynchronous HTTP POST Requests by Flow servers directly to your site, done as soon a transaction process ends in Flow servers. Think about them as notifications to your site.

<?php

$flow->setWebhooksUrls([
    'payment.urlConfirmation'       => 'https://myapp.com/webhooks/payment-created', 
    'refund.urlCallBack'            => 'https://myapp.com/webhooks/refund-created',  // The uppercase `B` is not a typo.
    'plan.urlCallback'              => 'https://finances.app.com/webhooks/plan-subscribed',
]);

As you can see, each Webhook correspond to an event in Flow:

  • A payment (or Payment Email) is completed.
  • A refund is completed.
  • A subscription to a Plan is paid.

If your application supports Event Handling, you can add your logic where these Webhooks are processed. Events Listeners may be: sending a receipt, reporting to finances, renewing the subscription, etc.

Ensure you have proper logic behind these Webhooks, because there is no guarantee the user browser will ever return to your app to see the transaction results. For example, this may happen if the user disconnects just before completing the transactions, like on the go from his phone or under unstable connection.

Webhook endpoint protection

To ensure endpoint protection for your application, you can append a random string to the Webhooks, set by you or as a default like you saw before. This ensures your application process async requests only made by Flow.

To append it, just set it with setWebhookSecret() along with your random string. The string will be appended automatically to the URL just before sending the HTTP POST Request.

<?php

// Set the Webhook Secret
$flow->setWebhookSecret('d6199909d0b5fdc22c9db625e4edf0d6');

$payment = $flow->payment()->commit([
    'urlConfirmation' => 'https://myapp.com/webhooks/payment-created',
    //...
]);

Once set and committed, Flow will issue a POST request using that string as a parameter.

POST https://myapp.com/flow/webhooks/payment-created?secret=d6199909d0b5fdc22c9db625e4edf0d6

Then, in your application, you can get the secret parameter and compare to the one persisted in your application. From there you can proceed if its the same, and abort if is not.

You could even control this before PHP read the Request using a custom rule in NGINX, Apache or similar.

NEVER USE YOUR SECRET KEY HERE! Always issue a different string for Webhooks Secrets. You can safely get a random string with bin2hex(random_bytes(16)).

Logging

If you don't want to log transactions at all, or you application doesn't support any kind of PSR3-compliant Logging, just pass the NullLogger. This is automatically done when using the make() without a third argument.

We use the Logger to log detailed errors and debugs, while leaving the Exceptions without any sensible information.

When using direct instantiation, ensure you're passing a LoggerInterface class. This will allow you to log Flow transactions into your application for informative purposes and errors.

<?php

namespace App;

use DarkGhostHunter\FlowSdk\Flow;
use Psr\Log\NullLogger;

$flow = new Flow(new NullLogger);

This package includes a basic logger called KLogger, which you can use to do basic logging to a directory of your choice:

<?php

namespace App;

use DarkGhostHunter\FlowSdk\Flow;
use Katzgrau\KLogger\Logger;

$logger = new Logger(__DIR__.'/../logs');

$flow = new Flow($logger);

Signing

This package automatically signs all the requests to Flow using your issued appKey and secret, and appends them into the Request payload just before going out to Flow, so you will never see them.

You don't have to manually sign or modify your transaction. Don't do this:

<?php

// Do this and a kitten will die
$customer = $flow->customer()->create([
    'apiKey'    => '1F90971E-8276-4713-97FF-2BLF5091EE3B',
    'name'      => 'John Doe',
    'email'     => 'johndoe@email.com',
    'secret'    => 'f8b45f9b8bcdb5702dc86a1b894492303741c405',
    'signature' => 'ZjhiNDVmOWI4YmNkYjU3MDJkYzg2YTF'
]);

Hopefully, signing will be deprecated in favor of an obligatory and well-configured HTTPS connection to secure MITM attacks, leaving just the apiKey as your secret.

Http Client

By default, this package uses Guzzle to make Requests and capture Responses from Flow. You shouldn't need anything more.

Just in case, you're free to use your own Client by setting an Adapter to sit between your Http Client and this package. Your adapter will need to handle GET and POST Requests, which will come from Flow signed and ready to be sent.

To use it, implement the Contracts\AdapterInterface.

<?php

namespace App;

use App\MyAdapter;
use DarkGhostHunter\FlowSdk\Flow;
use Psr\Log\NullLogger;

$flow = new Flow(new NullLogger);

$flow->setAdapter(new MyAdapter);

Using the Services

Services represent the different transaction types that you can interact within Flow, like payments, refunds, customers, etc. Each service makes a Resource, which can be committed, created, updated, deleted, and retrieved. This will depend on the service type, though.

Some Services do not support all of them. When trying to use a unavailable operation you will receive a BadMethodCallException.

Service instance

In this example, we will use the Payment Service. For simplicity, we will use the payment() method from the Flow instance.

<?php

namespace App;

use DarkGhostHunter\FlowSdk\Flow;

$flow = Flow::make('production', [
    'apiKey'    => '1F90971E-8276-4715-97FF-2BLG5030EE3B',
    'secret'    => 'f8b45f9b8bcdb5702dc86a1b894492303741c405',
]);

$payment = $flow->payment();

The $flow->payment() method returns a singleton, meaning it will always output the same Payment instance, which is saved inside the $flow instance. Of course, if you don't agree with this anti-pattern (which is just added for apps without proper Dependency Injection), you can instance it yourself old school:

<?php

namespace App;

use DarkGhostHunter\FlowSdk\Flow;
use DarkGhostHunter\FlowSdk\Services\Payment;

$flow = Flow::make('production', [
    'apiKey'    => '1F90971E-8276-4715-97FF-2BLG5030EE3B',
    'secret'    => 'f8b45f9b8bcdb5702dc86a1b894492303741c405',
]);

$payments = new Payment($flow);

// Save it to our Service Container
ServiceContainer::setService('payment', $payment);

Once the Payment instance is constructed, it will get all the settings from the Flow instance automatically.

Resources Attributes

All the attributes for each resource are documented on the Official Flow API. Refer to this to know what attribute does what.

This package does not change any attribute for the transactions, even if they have a typo, lack of consistency or ar just badly named for their purpose.

No promise, but in later versions this package may include attributes aliases for better understandability.

Commit vs Create (or Make-Save)

Between the different Services, you will see some using commit(), and others using create(). The difference between them is the response.

The commit() will return a Response from Flow with data related to the resource but unrelated to its attributes. For example, the Payments resources are committed, not created, because it returns a token and url, instead of the filled payment resource.

<?php

$payment = $flow->payments()->make([
    // ...
]);

$response = $payment->commit();

echo $response->url; // http://flow.cl/...
echo $response->flowOrder; // null

When a Response is received it's also saved inside the Resource, which you can get using getResponse(). By default, once the successful response has been received, commit() will return the saved response. You can force to commit again using forceCommit().

<?php

$payment = $flow->payments()->make([
    // ...
]);

$response = $payment->commit();

$newResponse = $payment->forceCommit();

The create(), along with get(), update() and delete(), will return the Resource from Flow. These methods will return booleans true or false when used inside the Resource, and they update the same instance instead of returning a new one.

<?php

$customer = $flow->customer()->make([
    'name' => 'John Doe'
    // ...
]);

$customer->email = 'johndoe@mail.com';

echo $customer->flowId; // null

echo $customer->save(); // true

echo $customer->flowId; // cus_123412