PHPackages                             rainwaves/payfast-payment - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Payment Processing](/categories/payments)
4. /
5. rainwaves/payfast-payment

ActiveLibrary[Payment Processing](/categories/payments)

rainwaves/payfast-payment
=========================

This is a PHP package for integrating with the PayFast.co.za payment gateway. It provides a convenient way to handle one-time payments and recurring billing in your PHP applications, with support for both vanilla PHP and Laravel.

v2.0.1(2w ago)01.3kMITPHPPHP ^8.2|^8.3|^8.4|^8.5CI passing

Since Jul 13Pushed 2w ago1 watchersCompare

[ Source](https://github.com/Magnificent-Big-J/payfast-payment)[ Packagist](https://packagist.org/packages/rainwaves/payfast-payment)[ RSS](/packages/rainwaves-payfast-payment/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (10)Dependencies (8)Versions (12)Used By (0)

Payfast Payment Package
=======================

[](#payfast-payment-package)

A PHP package for integrating with the PayFast payment gateway. Supports one-time payments, recurring subscriptions, and ITN (Instant Transaction Notification) validation — for both vanilla PHP and Laravel.

Installation
------------

[](#installation)

```
composer require rainwaves/payfast-payment
```

Compatibility
-------------

[](#compatibility)

PackagePHPLaravelv2.x8.2 – 8.512 – 13v1.7.x7.4 – 8.510 – 13Laravel 10+ requires PHP 8.1+. Laravel 12+ requires PHP 8.2+. Laravel 13+ requires PHP 8.3+.

Configuration
-------------

[](#configuration)

### Laravel

[](#laravel)

Publish the config file:

```
php artisan vendor:publish --tag=payfast-config
```

Then set your credentials in `.env`:

```
PAYFAST_MERCHANT_ID=your-merchant-id
PAYFAST_MERCHANT_KEY=your-merchant-key
PAYFAST_ENVIRONMENT=sandbox
PAYFAST_RETURN_URL=https://example.com/success
PAYFAST_CANCEL_URL=https://example.com/cancel
PAYFAST_NOTIFY_URL=https://example.com/notify
PAYFAST_PASS_PHRASE=your-payfast-passphrase
```

> **Note:** The legacy unprefixed env names (`MERCHANT_ID`, `MERCHANT_KEY`, `ENVIRONMENT`, etc.) still work as fallbacks, but `PAYFAST_` prefixed names are recommended to avoid conflicts with other packages.

### Vanilla PHP

[](#vanilla-php)

Pass a config array directly:

```
$config = [
    'merchant_id'  => 'your-merchant-id',
    'merchant_key' => 'your-merchant-key',
    'environment'  => 'sandbox',         // 'sandbox' or 'production'
    'return_url'   => 'https://example.com/success',
    'cancel_url'   => 'https://example.com/cancel',
    'notify_url'   => 'https://example.com/notify',
    'pass_phrase'  => 'your-payfast-passphrase',
];
```

Usage
-----

[](#usage)

### One-time Payment (Vanilla PHP)

[](#one-time-payment-vanilla-php)

```
require 'vendor/autoload.php';

use rainwaves\PayfastPayment\PayFast;

$payFast = new PayFast($config);

$input = [
    'amount'             => 100.00,
    'item_name'          => 'Test Product',
    'name_first'         => 'John',
    'name_last'          => 'Doe',
    'email_address'      => 'john@example.com',
    'm_payment_id'       => 'order-1234',
    'email_confirmation' => true,
    'custom_str1'        => 'internal-ref',
];

echo $payFast->makePaymentWithAForm($input)->createForm();
```

A hidden-field HTML form is returned, which submits the user to PayFast.

### One-time Payment (Laravel — dependency injection)

[](#one-time-payment-laravel--dependency-injection)

```
use rainwaves\PayfastPayment\Contract\PayFastInterface;

class PaymentController extends Controller
{
    public function __construct(private PayFastInterface $payFast) {}

    public function checkout(Request $request): string
    {
        $input = [
            'amount'        => 100.00,
            'item_name'     => 'Gold Plan',
            'email_address' => $request->user()->email,
            'm_payment_id'  => $request->user()->id,
        ];

        return $this->payFast->makePaymentWithAForm($input)->createForm();
    }
}
```

### Subscriptions

[](#subscriptions)

```
use rainwaves\PayfastPayment\PayFastSubscription;
use rainwaves\PayfastPayment\Model\Frequency;

$payFast = new PayFastSubscription($config);

$input = [
    'amount'                    => 100.00,
    'item_name'                 => 'Gold Plan',
    'billing_date'              => '2026-05-01',
    'recurring_amount'          => 100.00,
    'frequency'                 => Frequency::MONTHLY,
    'cycles'                    => 0,   // 0 = indefinite
    'subscription_notify_email'   => true,
    'subscription_notify_webhook' => true,
    'subscription_notify_buyer'   => true,
];

echo $payFast->createSubscriptionWithAForm($input)->createForm();
```

### Native v2 Client

[](#native-v2-client)

```
use rainwaves\PayfastPayment\Client\PayFastClient;
use rainwaves\PayfastPayment\Request\PauseSubscriptionRequest;

$payfast = PayFastClient::make($config);

$pause = $payfast->subscriptions()->pause(
    'subscription-token',
    new PauseSubscriptionRequest(1)
);

if ($pause->successful()) {
    // Update your application-owned subscription state.
}
```

Supported native subscription operations:

- fetch
- pause
- unpause
- cancel
- update
- ad hoc charge
- card-update link generation

### Optional Payment Fields

[](#optional-payment-fields)

FieldDescription`payment_method`Restrict to one method: `eft`, `cc`, `dc`, `bc`, `mp`, `mc`, `cd`, `sc``email_confirmation`Send buyer a confirmation email (`true`/`false`)`confirmation_address`Override email for confirmation`custom_int1..5`Integer custom fields (passed through ITN)`custom_str1..5`String custom fields, max 255 chars each### Frequency Constants

[](#frequency-constants)

```
Frequency::DAILY      // 1
Frequency::WEEKLY     // 2
Frequency::MONTHLY    // 3
Frequency::QUARTERLY  // 4
Frequency::BI_ANNUAL  // 5
Frequency::ANNUAL     // 6
```

ITN (Instant Transaction Notification) Validation
-------------------------------------------------

[](#itn-instant-transaction-notification-validation)

PayFast POSTs an ITN to your `notify_url` after each payment. **Validate all four checks** before acting on the notification.

```
use rainwaves\PayfastPayment\Itn\PayFastItnValidator;
use rainwaves\PayfastPayment\Model\Route;

$payload = $_POST;
$rawBody = file_get_contents('php://input');
$isSandbox = config('payfast.env') !== 'production';

$validator = new PayFastItnValidator(
    $payload,
    config('payfast.pass_phrase'),
    $rawBody
);

// 1. Source IP — always first
if (!$validator->validateSourceIp($_SERVER['REMOTE_ADDR'], $isSandbox)) {
    http_response_code(403);
    exit;
}

// 2. Signature
if (!$validator->validateSignature()) {
    http_response_code(400);
    exit;
}

// 3. Amount — compare against your stored order amount
if (!$validator->validateAmount('100.00')) {
    http_response_code(400);
    exit;
}

// 4. Merchant ID
if (!$validator->validateMerchantId(config('payfast.merchant_id'))) {
    http_response_code(400);
    exit;
}

// 5. (Optional) Server-side confirmation with PayFast endpoint
$validateUrl = Route::getValidationUrl(config('payfast.env'));
if (!$validator->validateWithPayFastEndpoint($validateUrl)) {
    http_response_code(400);
    exit;
}

// 6. Replay-attack protection — pf_payment_id must be unique
$paymentId = $validator->getPaymentId();
// … check that $paymentId hasn't been processed before …

// 7. Handle the response
$response = $validator->response();

if ($response->isComplete()) {
    // fulfil order
}
```

> **Tip:** PayFast requires a passphrase on your account for recurring billing. Without one, subscription signatures will fail.

> **Note on source IP validation:** `validateSourceIp()` resolves PayFast's own validation hostnames (`www`/`sandbox`/`w1w`/`w2w`.payfast.co.za) via live DNS at request time and checks the incoming IP against whatever they currently resolve to, matching PayFast's own reference implementation — it no longer checks against a hardcoded IP list (an earlier static-CIDR version of this check went stale as PayFast's real serving infrastructure grew). This means your notify-url handler needs outbound DNS resolution to work; if DNS is unreachable, every ITN is rejected (fail closed) rather than silently accepted.

### v2 Orchestrated ITN Validation

[](#v2-orchestrated-itn-validation)

```
use rainwaves\PayfastPayment\Client\PayFastClient;
use rainwaves\PayfastPayment\Request\ExpectedPayment;
use rainwaves\PayfastPayment\Support\Money;

$payfast = PayFastClient::make($config);

$result = $payfast->itn()->validate(
    payload: $_POST,
    rawBody: file_get_contents('php://input'),
    remoteIp: $_SERVER['REMOTE_ADDR'],
    expected: new ExpectedPayment('10000100', Money::zar('100.00')),
    confirmWithPayFast: true
);

if (!$result->valid()) {
    http_response_code(400);
    exit;
}
```

The host application still owns `pf_payment_id` replay protection, persistence, routes, controllers, authorization, and billing policy.

Privacy And POPIA
-----------------

[](#privacy-and-popia)

This package builds PayFast requests, validates ITNs, and calls PayFast subscription APIs. It does not create database tables, retain payment records, or provide privacy notices, retention controls, or data-subject request workflows.

Host applications remain responsible for lawful processing of personal information such as names, email addresses, payment references, subscription tokens, IP addresses, and ITN payloads. Store only the fields you need, protect merchant credentials and passphrases as secrets, redact diagnostic payloads before logging, and define retention/deletion rules that fit your POPIA and business obligations.

v2 Docs
-------

[](#v2-docs)

- [PayFast API contract](docs/payfast-api-contract.md)
- [Migration from v1 to v2](docs/migration-v1-to-v2.md)

Testing
-------

[](#testing)

```
vendor/bin/phpunit
```

License
-------

[](#license)

MIT

###  Health Score

52

—

FairBetter than 96% of packages

Maintenance97

Actively maintained with recent releases

Popularity19

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity69

Established project with proven stability

 Bus Factor1

Top contributor holds 76.8% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~124 days

Recently: every ~45 days

Total

10

Last Release

15d ago

Major Versions

v1.7.0 → v2.0.02026-07-28

PHP version history (3 changes)v1.0.0PHP ^7.4|^8.0|^8.1|^8.2

v1.7.0PHP ^7.4|^8.0|^8.1|^8.2|^8.3|^8.4|^8.5

v2.0.0PHP ^8.2|^8.3|^8.4|^8.5

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/30896750?v=4)[Joel Mnisi](/maintainers/Magnificent-Big-J)[@Magnificent-Big-J](https://github.com/Magnificent-Big-J)

---

Top Contributors

[![Magnificent-Big-J](https://avatars.githubusercontent.com/u/30896750?v=4)](https://github.com/Magnificent-Big-J "Magnificent-Big-J (53 commits)")[![mnisij](https://avatars.githubusercontent.com/u/14214514?v=4)](https://github.com/mnisij "mnisij (16 commits)")

### Embed Badge

![Health badge](/badges/rainwaves-payfast-payment/health.svg)

```
[![Health](https://phpackages.com/badges/rainwaves-payfast-payment/health.svg)](https://phpackages.com/packages/rainwaves-payfast-payment)
```

###  Alternatives

[linkxtr/laravel-qrcode

A clean, modern, and easy-to-use QR code generator for Laravel

3827.1k](/packages/linkxtr-laravel-qrcode)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
