PHP code example of square1 / laravel-mpp

1. Go to this page and download the library: Download square1/laravel-mpp library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

square1 / laravel-mpp example snippets


Route::get('/resource', MyPaidResource::class)
    ->middleware('mpp:0.50,USD');

#[RequiresPayment(amount: '5.00', currency: 'USD', grants: 10, scope: 'report.basic')]
public function report()
{
    // One $5 payment grants 10 accesses.
}

use Illuminate\Support\Facades\Route;

Route::get('/resource', fn () => response()->json(['result' => 'SOME_DATA']))
    ->middleware('mpp:1.00,USD');

// config/mpp.php
'methods' => [
    'stripe' => [
        'customer_resolver' => [\App\Mpp\StripeCustomerResolver::class, 'resolve'],
    ],
],

namespace App\Mpp;

use Illuminate\Http\Request;

class StripeCustomerResolver
{
    public function resolve(Request $request): ?string
    {
        return $request->user()?->stripe_customer_id;
    }
}

Route::get('/paid', fn () => response()->json(['data' => 'paid']))
    ->middleware('mpp:0.01,USD,method=tempo,scope=paid');

use Illuminate\Support\Facades\Route;

Route::get('/tempo-test', fn () => response()->json([
    'paid' => true,
    'at' => now()->toIso8601String(),
]))->middleware('mpp:0.01,USD,method=tempo,scope=tempo.test');

Route::get('/resource', MyPaidResource::class)
    ->middleware('mpp:0.50,USD,methods=stripe|tempo');

#[DiscoveryInfo(parameters: [
    'id' => ['example' => 1],
])]

use Square1\Mpp\Attributes\DiscoveryInfo;
use Square1\Mpp\Attributes\RequiresPayment;

#[RequiresPayment(amount: '0.50', currency: 'USD', methods: ['tempo', 'stripe'])]
#[DiscoveryInfo(
    summary: 'Clip a video',
    description: 'Returns a clip of the source video, starting at the requested offset.',
    priceNote: ['tempo' => 'On-chain, per clip.', 'stripe' => 'Card, per clip.'],
    tags: ['media'],
    request: ClipRequest::class,
    response: ['type' => 'object', '

Route::post('/clip', fn () => /* … */)
    ->middleware('mpp:0.50,USD')
    ->discovery(summary: 'Clip a video', priceNote: 'Per clip, whatever its length.');

'operations' => [
    'reports.show' => [
        'summary' => 'Fetch a report',
        'priceNote' => 'Per report.',
        'parameters' => ['year' => 'Four-digit year.'],
        'query' => ['format' => 'Output format.'],
    ],
],

public function rules(): array
{
    return [
        'url' => 'format' => '

#[DiscoveryInfo(response: [
    '200' => ['type' => 'object', 'properties' => ['url' => ['type' => 'string']]],
    '404' => ['description' => 'No such video.'],
])]

use Square1\Mpp\Discovery\ProvidesSchema;

final class ScoreSchema implements ProvidesSchema
{
    public static function schema(): array
    {
        return [
            'type' => 'object',
            '

final class ClipResult
{
    /** @param list<Scoreline> $scorelines */
    public function __construct(
        public readonly string $url,
        public readonly ClipFormat $format,      // a backed enum
        public readonly ClipSource $source,      // another data object
        public readonly ?string $session,        // 

#[DiscoveryInfo(response: [
    '200' => [
        'schema' => ClipResult::class,
        'headers' => ['X-Rate-Limit' => ['schema' => ['type' => 'integer']]],
    ],
])]

'

// config/mpp.php
'pipeline' => [
    [DiscoveryComponents::class, 'handle'],
],

class DiscoveryComponents
{
    public function handle(array $document): array
    {
        $document['components'] = ['schemas' => [/* … */]];

        return $document;
    }
}

Route::get('/resource', MyPaidResource::class)
    ->middleware('mpp:0.50,USD');

Route::get('/report', ReportController::class)
    ->middleware('mpp:5.00,USD,grants=10,scope=report.basic');

Route::get('/report', ReportController::class)
    ->middleware('mpp:report.basic');

use Square1\Mpp\Attributes\RequiresPayment;

class ReportController
{
    #[RequiresPayment(amount: '5.00', currency: 'USD', grants: 10, scope: 'report.basic')]
    public function __invoke()
    {
        // ...
    }
}

Route::get('/report', ReportController::class)->middleware('mpp');

#[RequiresPayment(amount: '0.50', currency: 'USD')]
public function latest()
{
    // ...
}

Route::get('/report', ReportController::class)
    ->middleware('mpp:scope=report');

#[RequiresPayment(scope: 'resource')]
public function latest()
{
    // Amount, currency, grants, and method come from config.
}

Route::get('/report', ReportController::class)
    ->middleware('mpp:5.00,USD,grants=10,scope=report.basic');

// config/mpp.php
'pricing' => [
    'resolvers' => [
        'tiered' => [\App\Mpp\Pricing\TieredPrice::class, 'price'],
    ],

    // Apply to every gated route, before any route-specific resolvers.
    'global' => [],
],

namespace App\Mpp\Pricing;

use Illuminate\Http\Request;
use Square1\Mpp\Payment\PaymentSpec;

class TieredPrice
{
    /** @return array<string, mixed>|null */
    public function price(Request $request, PaymentSpec $spec): ?array
    {
        return match ($request->user()?->tier) {
            'pro'     => ['amount' => '2.00'],
            'partner' => ['amount' => '2.00', 'grants' => 20, 'scope' => 'report.partner'],
            'staff'   => ['free' => true],
            default   => null,   // leave the route's own price alone
        };
    }
}

// $5 is the LIST PRICE. A caller pays it when `tiered` returns null.
// Recognised tiers get a discount off it.
Route::get('/report', ReportController::class)
    ->middleware('mpp:5.00,USD,scope=report,pricing=tiered');

#[RequiresPayment(amount: '5.00', scope: 'report', pricing: ['tiered'])]
public function show() { /* ... */ }

// Or on a price_book entry, so every route using the entry inherits it.
'price_book' => [
    'report.basic' => ['amount' => '5.00', 'currency' => 'USD', 'pricing' => ['tiered']],
],

// The route owns the default price. The resolver can overwrite it.
->middleware('mpp:5.00,USD,scope=report,pricing=tiered');

// The resolver owns the price.
->middleware('mpp:scope=report,pricing=tiered');

return ['free' => true];    // yes, serve this one for nothing
return ['amount' => '0'];   // throws

public function price(Request $request, PaymentSpec $spec): ?array
{
    return match ($request->user()?->tier) {

        // Only the price. Every other value on the route stays.
        'pro' => ['amount' => '2.00'],

        // Price, currency, bundle size and credit pool, all at once.
        'partner' => [
            'amount' => '18.00',
            'currency' => 'EUR',
            'grants' => 25,
            'scope' => 'everything.partner',
        ],

        // No charge. The same method, and the same return type. `free` is
        // another key. Do not add an 'amount' beside it, or the pair throws.
        'staff' => ['free' => true],

        // No opinion. This is NOT free: the price of the route stays.
        default => null,
    };
}

->middleware('mpp:5.00,USD,pricing=tiered|regional')

'partner' => ['amount' => '2.00', 'grants' => 20, 'scope' => 'report.partner'],

// config/mpp.php
'preconditions' => [
    'checks' => [
        'postexists'     => [\App\Mpp\Checks\PostExists::class, 'check'],
        'usernotblocked' => [\App\Mpp\Checks\UserNotBlocked::class, 'check'],
    ],

    // Run on every gated route, before any route-specific checks.
    'global' => ['usernotblocked'],
],

namespace App\Mpp\Checks;

use App\Models\Post;
use Illuminate\Http\Request;
use Square1\Mpp\Payment\PaymentSpec;
use Symfony\Component\HttpFoundation\Response;

class PostExists
{
    public function check(Request $request, PaymentSpec $spec): ?Response
    {
        return Post::find($request->route('post'))
            ? null
            : response()->json(['error' => 'No such post.'], 404);
    }
}

Route::get('/posts/{post}', ShowPost::class)
    ->middleware('mpp:1.00,USD,scope=post.view,preconditions=postexists');

#[RequiresPayment(amount: '1.00', scope: 'post.view', preconditions: ['postexists'])]
public function show() { /* ... */ }

'price_book' => [
    'report.basic' => ['amount' => '5.00', 'currency' => 'USD', 'grants' => 10],
],

Route::get('/report', ReportController::class)
    ->middleware('mpp:report.basic');

'price_book' => [
    'report.basic' => [
        'amount' => '5.00',
        'currency' => 'USD',
        'grants' => 10,
        'pricing' => ['tiered'],
        'preconditions' => ['usernotblocked'],
    ],
],

namespace App\Mpp;

use Square1\Mpp\Protocol\Challenge;
use Square1\Mpp\Protocol\Credential;
use Square1\Mpp\Settlement\SettlementResult;
use Square1\Mpp\Settlement\Verifier;
use Square1\Mpp\Support\Money;

final class AcmePayVerifier implements Verifier
{
    public function __construct(private readonly AcmePayClient $acme) {}

    public function verify(Credential $credential, Challenge $challenge, array $context = []): SettlementResult
    {
        $chargeId = $credential->proof;

        if ($chargeId === null || $chargeId === '') {
            return SettlementResult::failure('No AcmePay charge id presented.');
        }

        try {
            $charge = $this->acme->getCharge($chargeId);
        } catch (\Throwable $e) {
            return SettlementResult::failure('AcmePay lookup failed: '.$e->getMessage());
        }

        $expectedMinor = Money::toMinorUnits($challenge->amount, $challenge->currency);

        if ($charge->status !== 'succeeded'
            || $charge->amountMinor !== $expectedMinor
            || strtoupper($charge->currency) !== strtoupper($challenge->currency)) {
            return SettlementResult::failure('AcmePay charge does not match the challenge.');
        }

        return SettlementResult::settled(
            settlementRef: $charge->id,
            amountMinor: $expectedMinor,
            currency: $challenge->currency,
        );
    }
}

'methods' => [
    'acme' => [
        'verifier' => \App\Mpp\AcmePayVerifier::class,
        'payment_method_types' => ['acme'],
    ],
],
bash
php artisan vendor:publish --tag=mpp-config
bash
php artisan vendor:publish --tag=mpp-migrations
php artisan migrate
bash
CREDENTIAL=$(php -r 'echo rtrim(strtr(base64_encode(json_encode([
    "challenge" => [
        "id" => $argv[1], "realm" => "your-host",
        "method" => "stripe", "intent" => "charge",
        "request" => $argv[2], "expires" => $argv[3],
        "opaque" => $argv[5],
    ],
    "payload" => ["spt" => $argv[4]],
])), "+/", "-_"), "=");' "$CHALLENGE_ID" "$REQUEST_B64" "$EXPIRES" "$SPT" "$OPAQUE")

curl -si https://your-host/resource -H "Authorization: Payment $CREDENTIAL"
text
https://explore.testnet.tempo.xyz/address/0x...
bash
php artisan vendor:publish --tag=mpp-migrations
php artisan migrate