How To Build Your Own PHP Composer Package

A couple of months back, I wrote a PHP library that abstracts the API of Paystack (A Nigerian FinTech company) into a pluggable composer package. The library has since gotten more than 100 installs, and another package is dependent on it. Since then, a host of other payment systems/providers and other services have popped up, so I thought to write this tutorial or guide to building your own composer installable composer package.
So, off we go.

Directory Structure

The first thing to do is to create the directory that holds your package. It is also where you will develop the package from.
Usually, the basic directory structure for PHP packages looks like;

- parent_directory/
  - src/
  - tests/
  - composer.json
  - readme.md
  - changelog.md
  - contributing.md

The parent_directory holds all the files of the package.
The src (short for source) directory holds the package codes, the tests directory holds the package’s tests, composer.json describes your package.
readme.md contains the information about your package, forming part of its documentation.
changelog.md is a log or record of all notable changes made to a package. It is important to keep this as it helps users of your package to follow the changes you make to your package per version. More information about the format can be found here.
contributing.md explains how someone may contribute to your package. It ideally should contain information like the coding standards, submission and review process.

Composer

I won’t be demystifying composer in this article, but composer is key in helping you distribute your PHP library/SDK. The composer.json file describes your package and any dependencies it may need to both Packagist (the default composer packages repository) and Composer. Other actions/configurations – such as how your package should be autoloaded- are specified in this file. For your package though, few things are important and required in your composer.json file;

  • name: This describes the unique name of your package often in the form vendor/package_name. This format allows for uniqueness of the package incase the package name you decided on already exists. Vendor is usually the the name of the author or organization that made the package.
  • version: This describes the current version of your package. This is key because the version is what Packagist uses to resolve the version(s) of your package. Ideally, you should follow the Semantic version specification (http://semver.org)
  • description: This describes your package
  • require: This is a list of other packages your package depends on.
  • require-dev: This contains a list of packages your package depends on for its development. Typically, this contains your testing tools.
  • autoload: this describes how your package should be autoloaded. Composer supports both PSR-0 and PSR-4 autoloading.

Quick note: The difference between require and require-dev is that when your package is pulled in as a dependency or installed, only the dependencies in require will be pulled in along with it as they are “required” for the proper working of your package. However, during development of your package, “require-dev” dependencies will be pulled in as well because they are required for development.

At its barest minimum, your composer.json should look something like;

 {
    "name": "vendor/package_name",
    "version": "0.0.1"
    "description": "My PHP Package",
    "require": {
    },
    "require-dev": {
    },
    "autoload": {
        "psr-4": {
            "vendor\\package_name\\": "src/",
            "vendor\\package_name\\Tests\\": "Tests/"
        }
    }
}

At this point, what is left is to run a composer install to pull in your dependencies and write your package codes.

Testing your package

Often time during development, we want to test how our package integrates with other packages/projects when pulled in. Note that this is different from the very important unit tests.
One way is to “require” your package in another project locally. To do this, you need to specify a “repository” that points to the local file path of your package in the composer.json of that project, and require your package like so;

{
    "name": "test-php-project",
    "description": "A PHP project to test my package",
    "repositories": [
        {
            "type": "path",
            "url": "full/file/path/to/directory/containing/my_package"
        }
    ],
    "require": {
        "my/package": "*"
    }
}

This is simply saying to composer, “Please check this folder location too for packages during install”.

Other Important Things To Note

  • Unit testing in very important. Make sure to get good code coverage on your unit tests for all your classes and files. This helps build user confidence in your package.
    Travis CI is a service that should be integrated in your package for continuous integration. This will run your complete test suite when you or collaborators make pull requests and contributions, which will allow you quickly see if new changes break anything.
  • Follow the PHP FIG PSR standards, especially PSR-1, PSR-2, and PSR-4. This defines expectations that keep your package up to the community standards.

In the next part of this article, we’ll discuss publishing your package.

Some important links;

Loading Extra Files In Chrome Extensions

Many times when building chrome extensions, we’ve often found ourselves in situations where we have to add extra html to the web page. One way is to add the html on directly in the content_script js file, but I personally prefer not mixing up html and js in the same file. Also depending on the amount of html needed, it’s just be better to create a new html file for that purpose and then find a way to bring it into the content script.

To do this, first we need to create the file and then add it to our manifest file as a web accessible resource. This allows the file to be usable in the context of a web page.

"web_accessible_resources": [
    "newfile.html"
]

Next if the file uses extra javascript or css files, we can add those in the content_script tag of our manifest file. This allows the css/js file be loaded as well so they can work on in the context of web pages and have access to the DOM as well.

"content_scripts": [
    {
      "matches": [{...}],
      "js": [
        "jquery-3.1.1.min.js",
        "content-script.js"
      ],
      "css": [
        "styles.css",
      ]
    }
]

Now that we have our manifest file all setup properly, next is to append the page when an action such as a click of a button happens.

// content-script.js
$('body').on('click', '#add_page', function(e) {
    e.preventDefault();
    $.get(chrome.extension.getURL('newfile.html'), function(html) {
        $('body').append(html);
    });
});

From the above, chrome.extension.getURL loads the file by converting the relative path within the extension install directory to a fully-qualified URL. JQuery’s $.get loads the file and we append it to the web page’s body.

Thats all. I hope this helps.

Integrating mabiola/paystack-php-lib In Your PHP Projects – 1

A few weeks ago, I released a PHP Library or as one of my colleagues will call it, an SDK, for easily integrating Paystack in your PHP projects.

This is the first of a series of posts that will explain as simply as possible, how to use the library in your projects. Througout this series, we will be working with vanila PHP (A PHP project with no framework) so as not to restrict ourselves to a particular “box”.

In this first post, we’ll be setting up the sample project and doing the most basic operation you can do with Paystack – recieving payment from a client.

So to have a general idea of what we will be working on, we will create an order page, and when the user fills in their information and click a pay button, we redirect them to an authorization URL to provide their card details and make their payment. Paystack redirects back to a specified callback URL where we will verify payment and say thanks for a successful payment. Or not.

Creating the project

In this post, I won’t restrict you to my particular way of structuring my web projects, so i’m just going to say, create your project the way you will normally do. However, there are some important files we must both have.

1. A .env file
2. A composer.json file

Create these two files in your root directory and we are good to go.
In your .env file, copy and paste the following;
#PAYSTACK LIB MODE [test | live]
PAYSTACK_MODE = test
#YOUR PAYSTACK KEYS
PAYSTACK_LIVE_PUBLIC_KEY = my_paystack_live_public_keys
PAYSTACK_LIVE_SECRET_KEY = my_paystack_live_secret_key
PAYSTACK_TEST_PUBLIC_KEY = my_paystack_test_public_key
PAYSTACK_TEST_SECRET_KEY = my_paystack_test_secret_key

In your composer.json, add "mabiola/paystack-php-lib" : "~0.1" to your required dependencies

Add the end of this, your composer.json should look something like this;

composer.json

Now, do a composer install, which will download the required package(s). If you don’t have composer on your PC, look at this to see how to install composer.

After you composer install, check your project’s root, you should see a vendor folder where the dependencies your project required are installed.

Now, lets head over to https://paystack.co to sign up so we can replace those keys we placed in our .env.

After signing up, navigate to settings, then to the Developer/API tab to retrieve your API keys.

paystack-settings-page

Under the web hook section, update the Test Callback URL to your preferred call URL. For me in this tutorial, I’ll use http://localhost/paystacksample/payment_verification.php which is where I’ll be verifying if payment is successful or not.

Looks like we all set up and ready to go.

Create A Payment Order Page

On the index.php file, Add the following to create a payment form.

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="description" content="">
        <meta name="author" content="">

        <title>Paystack Sample</title>

        <!-- Bootstrap Core CSS -->
        <link href="css/bootstrap.min.css" rel="stylesheet">

        <!-- Custom CSS -->
        <link href="css/full-width-pics.css" rel="stylesheet">

        <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
        <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
        <!--[if lt IE 9]>
        <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
        <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
        <![endif]-->
    </head>
    <body>
        <!-- Navigation -->
        <nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
            <div class="container">
                <!-- Brand and toggle get grouped for better mobile display -->
                <div class="navbar-header">
                    <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
                        <span class="sr-only">Toggle navigation</span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                    </button>
                    <a class="navbar-brand" href="#">Paystack Sample</a>
                </div>
                <!-- Collect the nav links, forms, and other content for toggling -->
                <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                    <ul class="nav navbar-nav">
                        <li>
                            <a href="#">About</a>
                        </li>
                        <li>
                            <a href="#">Services</a>
                        </li>
                        <li>
                            <a href="#">Contact</a>
                        </li>
                    </ul>
                </div>
                <!-- /.navbar-collapse -->
            </div>
        <!-- /.container -->
        </nav>
        <!-- Full Width Image Header with Logo -->
        <!-- Image backgrounds are set within the full-width-pics.css file. -->
        <header class="image-bg-fluid-height">

        </header>
        <!-- Content Section -->
        <section>
            <div class="container">
                <div class="row">
                    <div class="col-lg-12">
                        <h1 class="section-heading">Make Payment (Total Cost: N 200.00)</h1>
                    </div>
                    <div class="col-md-6">
                        <form class="form-horizontal" role="form" action="create_payment.php" method="post">
                            <div class="form-group">
                                <label class="control-label col-sm-2" for="email">Email:</label>
                                <div class="col-sm-10">
                                    <input type="email" class="form-control" id="email" name="email" placeholder="Enter email">
                                </div>
                            </div>
                            <div class="form-group">
                                <div class="col-sm-offset-2 col-sm-10">
                                    <button type="submit" class="btn btn-success">Pay</button>
                                </div>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </section>

        <!-- Footer -->
        <footer>
            <div class="container">
                <div class="row">
                    <div class="col-lg-12">
                        <p>Copyright &copy; 2016</p>
                    </div>
                </div>
                <!-- /.row -->
            </div>
            <!-- /.container -->
        </footer>

        <!-- jQuery -->
        <script src="js/jquery.js"></script>

        <!-- Bootstrap Core JavaScript -->
        <script src="js/bootstrap.min.js"></script>
    </body>
</html>

Create A Transaction Generation Page

Now, we create a file (create_payment.php) that will handle our form action of generating an authorization url and redirecting to paystack when the pay button is clicked on our index page.

<?php
//Load composer autoload
require_once __DIR__ . '/vendor/autoload.php';
//load environment variables
(new \Dotenv\Dotenv(__DIR__))->load();

//get customer email
$customer_email = $_POST['email'];

//create paystack lib object
$paystack_lib_object = \MAbiola\Paystack\Paystack::make();

//create transaction
try {
    $authorization = $paystack_lib_object->startOneTimeTransaction('20000', $customer_email);
    //we should probably save the reference and email here so we can match/update records
    //redirect to payment authorization URL
    header('Location: ' . $authorization['authorization_url']);
} catch (Exception $e) {
    header("Location: error.php?error={$e->getMessage()}");
}

paystack-payment-page-after-redirect

Verifying Transaction
After the customers pays, they’ll be redirected from the Paystack payment page to our set redirect callback. This is where we’ll verify the transaction and if true, we’ll say thanks to our customer.

<?php
//Load composer autoload
require_once __DIR__ . '/vendor/autoload.php';
//load environment variables
(new \Dotenv\Dotenv(__DIR__))->load();


try {
    //create paystack lib object
    $paystack_lib_object = \MAbiola\Paystack\Paystack::make();
    $verification = $paystack_lib_object->verifyTransaction($_GET['trxref']);
    //if verification successful
    if ($verification) {
        //update customer records in db, probably add authorization for next time

        //redirect to a thank you page
        header('Location: thank_you.php');
    } else {
        header('Location: error.php');
    }

} catch (Exception $e) {
    header("Location: error.php?error={$e->getMessage()}");
}

Conclusion

That basically covers using the library. The full source code of the tutorial can be found on Github here.

Please don’t hesitate to comment below.

Thank you.