Installation and Setup
composer require guzzlehttp/guzzle
# Add to .env:
PAYCHAINLY_API_KEY=your_key
PAYCHAINLY_WEBHOOK_SECRET=your_secret
PAYCHAINLY_BASE_URL=https://paychainly.com
Service Class
namespace App\Services;
use Illuminate\Support\Facades\Http;
class PaychainlyService {
protected string $apiKey;
protected string $baseUrl;
public function __construct() {
$this->apiKey = config('services.paychainly.key');
$this->baseUrl = config('services.paychainly.url');
}
public function createSession(string $userId, float $amount): array {
return Http::withHeaders(['x-api-key' => $this->apiKey])
->post("{$this->baseUrl}/api/v1/addresses/start-session", [
'userId' => $userId, 'amount' => $amount,
])->throw()->json();
}
}
Webhook Controller
class PaychainlyWebhookController extends Controller {
public function handle(Request $request) {
$signature = $request->header('x-signature');
$payload = $request->all();
$msg = implode('|', [
$payload['event'], $payload['txHash'], $payload['fromAddress'],
$payload['toAddress'], $payload['amount'], $payload['blockNumber'],
$payload['timestamp'], $payload['userId'],
]);
$expected = hash_hmac('sha256', $msg, config('services.paychainly.secret'));
if (!hash_equals($expected, $signature)) abort(401);
ProcessPayment::dispatch($payload);
return response()->json(['ok' => true]);
}
}
Queue Job
class ProcessPayment implements ShouldQueue {
public function handle(): void {
Payment::firstOrCreate(
['tx_hash' => $this->payload['txHash']],
['order_id' => $this->payload['userId'],
'amount' => $this->payload['amount'],
'status' => 'confirmed']
);
}
}