Basic Usage
Getting started with Orchestration in your Laravel application is straightforward. This guide will walk you through the process of setting up a basic service, making your first service request, and handling the response. For this example, we'll use a hypothetical checkout service integration.
Step 1: Orchestrate the Service
The first step is to define a service. If you haven't done so yet, follow the installation guide to set up a 'checkout' service along with a provider and a merchant.
php artisan orchestrate:service
Then, configure your service accordingly by following our configuration guide.
return [
'defaults' => [
'provider' => 'stripe',
'merchant' => 'payavel',
],
'providers' => [
'stripe' => [
'gateway' => \App\Services\Checkout\StripeCheckoutRequest::class,
'base_url' => 'https://api.stripe.com',
],
],
'merchants' => [
'payavel' => [
'providers' => [
'stripe' => [
'api_key' => '...',
'api_key' => env('STRIPE_PAYAVEL_API_KEY'),
'account_id' => env('STRIPE_PAYAVEL_ACCOUNT_ID'),
],
],
],
],
];
Step 2: Prepare the Gateway
Now let's establish the gateway for your service, which acts as the bridge between your application and the service provider. The gateway encapsulates all the necessary logic for communicating with the provider's API, including setting up HTTP requests, handling authentication, and defining the base URL.
namespace App\Services\Checkout;
use App\Services\Checkout\Contracts\CheckoutRequester;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Http;
use Payavel\Orchestration\ServiceRequest;
class StripeCheckoutRequest extends ServiceRequest implements CheckoutRequester
{
private $http;
protected function setUp(): void
{
//
$this->http = Http::asForm()
->baseUrl(Config::get('checkout.providers.stripe.base_url'))
->withToken(
Config::get("checkout.merchants.{$this->merchant->getId()}.providers.stripe.api_key")
)
->withHeader(
'Stripe-Account',
Config::get("checkout.merchants.{$this->merchant->getId()}.providers.stripe.account_id")
);
}
}
This setup process not only encapsulates the configuration necessary for the specific service provider but also illustrates how orchestration's flexible architecture allows for easy adaptation to different service requirements.
Step 3: Define the Service Contracts
Before implementing the logic of your service, start by defining the contracts for both requests and responses. These contracts serve as blueprints for how your service interacts with various providers.
Request Contract
Update the "app/Services/Checkout/Contracts/CheckoutRequester" interface with actions the service should perform.
namespace App\Services\Checkout\Contracts;
interface CheckoutRequester
{
//
public function charge(array $data): CheckoutResponder;
}
Response Contract
Similarly, update the "app/Services/Checkout/Contracts/CheckoutResponder" interface. This ensures that your service returns a consistent response structure, regardless of the underlying provider.
namespace App\Services\Checkout\Contracts;
interface CheckoutResponder
{
//
public function chargeResponse(): array;
}
Step 4: Implement the Contracts
After defining the service contracts, we shall implement them with actual logic that interacts with your service provider. This involves creating classes that fulfill the defined interfaces, enabling your service to perform actions and return structured responses.
The following implementations illustrate how orchestration facilitates the integration of complex service interactions into your Laravel application, abstracting away the direct API calls and responses into a more manageable and consistent format.
Service Request Implementation
namespace App\Services\Checkout;
use App\Services\Checkout\Contracts\CheckoutRequester;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Http;
use Payavel\Orchestration\ServiceRequest;
class StripeCheckoutRequest extends ServiceRequest implements CheckoutRequester
{
private $http;
protected function setUp(): void
{
$this->http = Http::asForm()
->baseUrl(Config::get('checkout.providers.stripe.base_url'))
->withToken(
Config::get("checkout.merchants.{$this->merchant->getId()}.providers.stripe.api_key")
)
->withHeader(
'Stripe-Account',
Config::get("checkout.merchants.{$this->merchant->getId()}.providers.stripe.account_id")
);
}
public function charge(array $data): StripeCheckoutResponse
{
$response = $this->http->post('v1/payment_intents', [
'amount' => $data['amount'],
'currency' => $data['currency'] ?? 'usd',
'payment_method' => $data['paymentMethod']['nonce'],
'confirm' => 'true',
'automatic_payment_methods' => [
'enabled' => 'true',
'allow_redirects' => 'never',
],
]);
return new StripeCheckoutResponse($response);
}
}
Service Response Implementation
namespace App\Services\Checkout;
use App\Services\Checkout\Contracts\CheckoutResponder;
use Payavel\Orchestration\ServiceResponse;
class StripeCheckoutResponse extends ServiceResponse implements CheckoutResponder
{
protected $successStatuses = [200];
private $decodedResponse;
/**
* Set up the response.
*
* @return void
*/
protected function setUp()
{
//
$this->decodedResponse = json_decode($this->rawResponse, true);
}
/**
* Determines the status code based on the request's raw response.
*
* @return int
*/
public function getStatusCode()
{
//
return $this->rawResponse->getStatusCode();
}
/**
* Get a string representation of the response's status.
*
* @return string|null
*/
public function getStatusMessage()
{
//
return $this->isSuccessful()
? 'Success'
: 'Error';
}
/**
* Get a description of the response's status.
*
* @return string|null
*/
public function getStatusDescription()
{
//
return $this->isSuccessful()
? 'The request was successful.'
: 'Something went wrong.';
}
public function chargeResponse(): array
{
return [
'reference' => $this->decodedResponse['id'],
'amount' => $this->decodedResponse['amount'],
];
}
}
Step 5: Use the Service
Now that you have your service and its contracts implemented, it's time to put everything into action. This step demonstrates how to use the orchestrated service within a single action controller to process a checkout action based on a customer's cart.
namespace App\Http\Controllers;
use App\Models\Cart;
use App\Models\Order;
use Illuminate\Http\Request;
use Payavel\Orchestration\Service;
class CheckoutController extends Controller
{
private $checkout;
public function __construct()
{
$this->checkout = new Service('checkout');
}
public function __invoke(Request $request, Cart $cart)
{
$request->validate([
'paymentMethod.nonce' => ['required', 'string', 'max:255'],
]);
$response = $this->checkout->charge([
'amount' => $cart->getTotal(),
'paymentMethod' => [
'nonce' => $request->input('paymentMethod.nonce'),
],
]);
if ($response->isNotSuccessful()) {
return response()->json([
'code' => $response->statusCode,
'message' => $response->statusMessage,
])->setStatusCode(424);
}
$order = Order::createFromCheckout($cart, $response);
return response()->json($order)->setStatusCode(201);
}
}
// routes/api.php
Route::post('carts/{cart}/checkout', CheckoutController::class)->name('carts.checkout');
Next Steps
By following this guide, you've learned how to orchestrate a service, define and implement service contracts, and effectively use the orchestrated service in a real-world scenario.
Ready to dive deeper? Consider exploring more sophisticated features of orchestration, including advanced service mocking strategies, and dynamic service configuration based on runtime conditions.