Installation:
composer require borsaco/coinbase-bundle
Ensure Borsaco\CoinbaseBundle\CoinbaseBundle is enabled in config/bundles.php.
Configuration:
Create config/packages/coinbase.yaml with your API credentials:
coinbase_commerce:
api:
key: "%env(COINBASE_API_KEY)%"
version: "2018-03-22"
webhook:
secret: "%env(COINBASE_WEBHOOK_SECRET)%"
Store secrets in .env (never commit .env to version control).
First Use Case:
Inject CoinbaseHandler into a controller to create a charge:
use Borsaco\CoinbaseBundle\Handler\CoinbaseHandler;
#[Route('/charge', name: 'create_charge')]
public function createCharge(CoinbaseHandler $handler): JsonResponse
{
$charge = $handler->createCharge([
'name' => 'Product Purchase',
'description' => 'Payment for product XYZ',
'local_price' => ['amount' => 10.99, 'currency' => 'USD'],
'metadata' => ['user_id' => 123],
]);
return $this->json($charge);
}
Charge Creation:
Use CoinbaseHandler::createCharge() with an array or Charge object. Always include:
name (string)description (string)local_price (array with amount and currency)metadata (for tracking), pricing_type (fixed or dynamic).Example with dynamic pricing:
$charge = $handler->createCharge([
'name' => 'Subscription',
'description' => 'Monthly plan',
'pricing_type' => 'dynamic',
'pricing_data' => [
'price_per_unit' => ['amount' => 9.99, 'currency' => 'USD'],
'unit' => 'month',
],
]);
Webhook Handling: Validate and process Coinbase webhooks in a dedicated controller:
#[Route('/webhook', name: 'coinbase_webhook', methods: ['POST'])]
public function handleWebhook(Request $request, CoinbaseHandler $handler): Response
{
$payload = json_decode($request->getContent(), true);
$secret = $request->headers->get('X-CC-Webhook-Signature');
if ($handler->validateWebhook($payload, $secret)) {
// Process event (e.g., charge:confirmed)
$this->processEvent($payload['type'], $payload['data']);
}
return new Response('OK', 200);
}
Retrieving Charges: Fetch charges by ID or filter by metadata:
// Get a single charge
$charge = $handler->getCharge('charge_id_here');
// List charges with metadata filter
$charges = $handler->listCharges(['metadata.user_id' => 123]);
Refunds: Refund a charge partially or fully:
$refund = $handler->createRefund('charge_id_here', [
'amount' => 5.00, // Partial refund
'reason' => 'Customer requested partial refund',
]);
$builder->add('charge_name', TextType::class);
$builder->add('charge_description', TextareaType::class);
$builder->add('charge_amount', MoneyType::class, [
'currency' => 'USD',
'scale' => 2,
]);
charge:confirmed) using Symfony’s event dispatcher.CoinbaseHandler in tests:
$handler = $this->createMock(CoinbaseHandler::class);
$handler->method('createCharge')->willReturn(['id' => 'test_charge']);
API Key Exposure:
coinbase.yaml. Use %env() or Symfony’s parameter bag.Commerce.Charges:Read, Commerce.Charges:Create).Webhook Validation:
CoinbaseHandler::validateWebhook(). Skipping this risks processing fake events.Idempotency:
idempotency_key parameter in createCharge() to avoid duplicate charges if the same request is retried.$charge = $handler->createCharge($data, 'unique_key_123');
Currency and Amount Precision:
10.99, not 10 or 10.990).number_format() or Symfony’s Money component to ensure consistency.Rate Limits:
Deprecated API Version:
2018-03-22, which may not support all latest features. Check Coinbase’s API docs for updates.Enable Debug Mode:
Set COINBASE_DEBUG=true in .env to log raw API responses. The bundle will output requests/responses to var/log/dev.log.
Common Errors:
| Error | Solution |
|---|---|
Invalid API key |
Verify coinbase.yaml and ensure the key has Commerce permissions. |
Webhook signature mismatch |
Regenerate the webhook secret in Coinbase Dashboard and update config. |
Amount must be positive |
Ensure local_price.amount is > 0. |
Charge not found |
Double-check the charge ID and ensure it exists in Coinbase Dashboard. |
Custom Charge Objects:
Extend Borsaco\CoinbaseBundle\Model\Charge to add domain-specific fields:
class CustomCharge extends Charge
{
private $userId;
public function setUserId(int $userId): self
{
$this->userId = $userId;
$this->setMetadata(['user_id' => $userId]);
return $this;
}
}
Then pass the object to createCharge().
Event Dispatcher: Integrate with Symfony’s event system to trigger custom logic on Coinbase events:
// In services.yaml
Borsaco\CoinbaseBundle\EventListener\CoinbaseWebhookListener:
tags:
- { name: kernel.event_listener, event: coinbase.webhook, method: onWebhook }
Async Processing: Use Symfony Messenger to process webhooks asynchronously:
$message = new CoinbaseWebhookMessage($payload);
$this->messageBus->dispatch($message);
Testing Helper:
Create a test double for CoinbaseHandler to simulate API responses:
class CoinbaseHandlerStub extends CoinbaseHandler
{
public function __construct()
{
$this->client = $this->createMock(\GuzzleHttp\Client::class);
}
public function createCharge(array $data, ?string $idempotencyKey = null)
{
return ['id' => 'test_charge_' . uniqid()];
}
}
How can I help you explore Laravel packages today?