Expose php artisan etsy:mcp as JSON-RPC stdio next to laravel-boost, reuse the stored HickLab3D shop connection, create listings as drafts only, and skip Etsy v3 conversation send because that API does not exist.
361 lines
22 KiB
PHP
361 lines
22 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\EtsyConnection;
|
|
use App\Models\IntegrationSetting;
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Tests\TestCase;
|
|
|
|
class IntegrationsTest extends TestCase
|
|
{
|
|
use LazilyRefreshDatabase;
|
|
|
|
public function test_regular_user_cannot_begin_etsy_oauth(): void
|
|
{
|
|
$this->actingAs(User::factory()->create())->post(route('etsy.connect'))->assertForbidden();
|
|
}
|
|
|
|
public function test_etsy_callback_rejects_mismatched_state_without_exchanging_token(): void
|
|
{
|
|
Http::preventStrayRequests();
|
|
$admin = User::factory()->create(['role' => 'admin']);
|
|
|
|
$this->actingAs($admin)->withSession(['etsy_oauth' => ['state' => 'expected', 'verifier' => 'verifier', 'started' => time()]])
|
|
->get(route('etsy.callback', ['state' => 'wrong', 'code' => 'code']))
|
|
->assertRedirect(route('etsy.index'))->assertSessionHasErrors('etsy');
|
|
Http::assertNothingSent();
|
|
}
|
|
|
|
public function test_etsy_callback_saves_encrypted_tokens_and_shows_orders(): void
|
|
{
|
|
Http::preventStrayRequests();
|
|
Http::fake([
|
|
'https://api.etsy.com/v3/public/oauth/token' => Http::response(['access_token' => '123.token', 'refresh_token' => '123.refresh', 'expires_in' => 3600, 'scope' => 'listings_r listings_w transactions_r transactions_w shops_r']),
|
|
'https://api.etsy.com/v3/application/users/123/shops' => Http::response(['shop_id' => 42, 'shop_name' => 'HickLab3D']),
|
|
'https://api.etsy.com/v3/application/shops/42/receipts*' => Http::response(['results' => [['receipt_id' => 77, 'name' => 'Buyer']]]),
|
|
'https://api.etsy.com/v3/application/shops/42/listings*' => Http::response(['results' => [['title' => '3D print', 'quantity' => 5]]]),
|
|
]);
|
|
config()->set('services.etsy', ['keystring' => 'key', 'shared_secret' => 'secret', 'redirect_uri' => 'https://example.test/etsy/callback']);
|
|
$admin = User::factory()->create(['role' => 'admin']);
|
|
|
|
$this->actingAs($admin)->withSession(['etsy_oauth' => ['state' => 'expected', 'verifier' => 'verifier', 'started' => time()]])
|
|
->get(route('etsy.callback', ['state' => 'expected', 'code' => 'code']))
|
|
->assertRedirect(route('etsy.index'));
|
|
$connection = EtsyConnection::firstOrFail();
|
|
$this->assertSame('123.token', $connection->access_token);
|
|
$this->assertNotSame('123.token', $connection->getRawOriginal('access_token'));
|
|
$this->assertSame('listings_r listings_w transactions_r transactions_w shops_r', $connection->scopes);
|
|
$this->actingAs($admin)->get(route('etsy.index'))->assertOk()->assertSee('3D print')->assertSee('#77');
|
|
Http::assertSentCount(4);
|
|
}
|
|
|
|
public function test_bambuddy_replaces_local_printer_status(): void
|
|
{
|
|
Http::preventStrayRequests();
|
|
Http::fake([
|
|
'https://3d.example.test/api/v1/printers/' => Http::response([['id' => 1, 'name' => 'X1 Carbon', 'status' => 'printing']]),
|
|
'https://3d.example.test/api/v1/printers/1/status' => Http::response(['connected' => true, 'state' => 'RUNNING', 'progress' => 45, 'temperatures' => ['nozzle' => 220, 'bed' => 60]]),
|
|
]);
|
|
config()->set('services.bambuddy', ['url' => 'https://3d.example.test', 'api_key' => 'read-key']);
|
|
|
|
$this->actingAs(User::factory()->create())->get(route('printers.index'))->assertOk()->assertSee('X1 Carbon')->assertSee('45%');
|
|
Http::assertSent(fn ($request) => $request->hasHeader('X-API-Key', 'read-key'));
|
|
}
|
|
|
|
public function test_shipping_quote_sends_parcel_to_parcel2go(): void
|
|
{
|
|
$this->pendingEtsyOrder();
|
|
Http::preventStrayRequests();
|
|
Http::fake([
|
|
'https://auth.example.test/token' => Http::response(['access_token' => 'test-token']),
|
|
'https://shipping.example.test/api/quotes' => Http::response(['Quotes' => [['Service' => ['Name' => 'Evri', 'Slug' => 'evri-collection', 'CollectionType' => 'Collection'], 'TotalPrice' => 5.5]]]),
|
|
'https://shipping.example.test/api/me/detail' => Http::response([]),
|
|
'https://shipping.example.test/api/me/prepay/balance' => Http::response([]),
|
|
'https://shipping.example.test/api/me/orders/detail' => Http::response(['Orders' => []]),
|
|
]);
|
|
config()->set('services.parcel2go', ['client_id' => 'id', 'client_secret' => 'secret', 'auth_url' => 'https://auth.example.test/token', 'base_url' => 'https://shipping.example.test/api']);
|
|
$admin = User::factory()->create(['role' => 'admin']);
|
|
$details = ['receipt_id' => 77, 'delivery_postcode' => 'SW1A 1AA', 'weight' => 2, 'length' => 10, 'width' => 10, 'height' => 10];
|
|
|
|
$this->actingAs($admin)->post(route('shipping.quote'), $details)->assertRedirect(route('shipping'));
|
|
$this->actingAs($admin)->get(route('shipping'))->assertOk()->assertSee('Evri')->assertSee('£5.50')->assertDontSee('name="delivery_postcode"', false);
|
|
Http::assertSent(fn ($request) => $request->url() === 'https://shipping.example.test/api/quotes' && $request['Parcels'][0]['Weight'] === 2 && $request['DeliveryAddress']['Postcode'] === 'M1 1AE' && $request['CollectionAddress']['Postcode'] === 'SW1A 1AA');
|
|
}
|
|
|
|
public function test_shipping_page_works_with_public_api_scope_without_profile_access(): void
|
|
{
|
|
Http::preventStrayRequests();
|
|
Http::fake(['https://auth.example.test/token' => function ($request) {
|
|
return $request['scope'] === 'public-api'
|
|
? Http::response(['access_token' => 'test-token'])
|
|
: Http::response(['error' => 'invalid_scope'], 400);
|
|
},
|
|
'https://shipping.example.test/api/me/orders/detail' => Http::response([], 403),
|
|
'https://shipping.example.test/api/me/detail' => Http::response([], 403),
|
|
'https://shipping.example.test/api/me/prepay/balance' => Http::response([], 403),
|
|
]);
|
|
config()->set('services.parcel2go', [
|
|
'client_id' => 'id', 'client_secret' => 'secret',
|
|
'auth_url' => 'https://auth.example.test/token',
|
|
'base_url' => 'https://shipping.example.test/api',
|
|
'scope' => 'public-api',
|
|
]);
|
|
|
|
$this->actingAs(User::factory()->create(['role' => 'admin']))->get(route('shipping'))
|
|
->assertOk()->assertSee('Pending Etsy orders')->assertSee('Parcel2Go denied shipment history')
|
|
->assertDontSee('Parcel2Go is unavailable');
|
|
Http::assertSentCount(4);
|
|
}
|
|
|
|
public function test_shipping_shows_existing_shipments_when_basic_access_can_read_history(): void
|
|
{
|
|
Http::preventStrayRequests();
|
|
Http::fake([
|
|
'https://auth.example.test/token' => Http::response(['access_token' => 'test-token']),
|
|
'https://shipping.example.test/api/me/orders/detail' => Http::response(['Orders' => [[
|
|
'OrderLineId' => 123, 'Courier' => 'Evri', 'Service' => 'Standard',
|
|
'Tracking' => ['Delivered' => true], 'DeliveryAddress' => ['ContactName' => 'Buyer'],
|
|
'EstimatedDeliveryDate' => '2026-10-15', 'Hash' => 'hash',
|
|
]]]),
|
|
'https://shipping.example.test/api/me/detail' => Http::response(['Forename' => 'Lee', 'Surname' => 'Hickman']),
|
|
'https://shipping.example.test/api/me/prepay/balance' => Http::response(['Balance' => 12.5]),
|
|
]);
|
|
config()->set('services.parcel2go', [
|
|
'client_id' => 'id', 'client_secret' => 'secret',
|
|
'auth_url' => 'https://auth.example.test/token',
|
|
'base_url' => 'https://shipping.example.test/api',
|
|
'scope' => 'public-api',
|
|
]);
|
|
|
|
$this->actingAs(User::factory()->create(['role' => 'admin']))->get(route('shipping'))
|
|
->assertOk()->assertSee('Existing shipments')->assertSee('Delivered')->assertSee('Buyer')
|
|
->assertSee('Lee Hickman')->assertSee('12.50')
|
|
->assertDontSee('Parcel2Go denied shipment history');
|
|
Http::assertSent(fn ($request) => $request->url() === 'https://shipping.example.test/api/me/detail' && $request->hasHeader('Authorization', 'Bearer test-token'));
|
|
Http::assertSent(fn ($request) => $request->url() === 'https://shipping.example.test/api/me/prepay/balance' && $request->hasHeader('Authorization', 'Bearer test-token'));
|
|
Http::assertSentCount(4);
|
|
}
|
|
|
|
public function test_shipping_rejects_a_quote_for_an_order_that_is_not_pending(): void
|
|
{
|
|
$this->pendingEtsyOrder();
|
|
Http::preventStrayRequests();
|
|
$admin = User::factory()->create(['role' => 'admin']);
|
|
|
|
$this->actingAs($admin)->post(route('shipping.quote'), [
|
|
'receipt_id' => 999, 'weight' => 2, 'length' => 10, 'width' => 10, 'height' => 10,
|
|
'delivery_postcode' => 'SW1A 1AA',
|
|
])->assertRedirect(route('shipping'))->assertSessionHasErrors('quote');
|
|
Http::assertSentCount(1);
|
|
}
|
|
|
|
public function test_shipping_falls_back_to_basic_access_when_parcel2go_rejects_profile_scope(): void
|
|
{
|
|
Http::preventStrayRequests();
|
|
Http::fake(['https://auth.example.test/token' => function ($request) {
|
|
return $request['scope'] === 'public-api'
|
|
? Http::response(['access_token' => 'test-token'])
|
|
: Http::response(['error' => 'invalid_scope'], 400);
|
|
},
|
|
'https://shipping.example.test/api/me/orders/detail' => Http::response([], 403),
|
|
'https://shipping.example.test/api/me/detail' => Http::response([], 403),
|
|
'https://shipping.example.test/api/me/prepay/balance' => Http::response([], 403),
|
|
]);
|
|
config()->set('services.parcel2go', [
|
|
'client_id' => 'id', 'client_secret' => 'secret',
|
|
'auth_url' => 'https://auth.example.test/token',
|
|
'base_url' => 'https://shipping.example.test/api',
|
|
'scope' => 'public-api my-profile',
|
|
]);
|
|
|
|
$this->actingAs(User::factory()->create(['role' => 'admin']))->get(route('shipping'))
|
|
->assertOk()->assertSee('Parcel2Go denied shipment history')
|
|
->assertDontSee('Parcel2Go is unavailable');
|
|
Http::assertSentCount(5);
|
|
}
|
|
|
|
public function test_shipping_label_is_downloaded_for_an_order_in_account_history(): void
|
|
{
|
|
Http::preventStrayRequests();
|
|
Http::fake([
|
|
'https://auth.example.test/token' => Http::response(['access_token' => 'test-token']),
|
|
'https://shipping.example.test/api/me/orders/detail' => Http::response(['Orders' => [['OrderLineId' => 123, 'Hash' => 'order-hash']]]),
|
|
'https://shipping.example.test/api/labels/123*' => Http::response(['Base64EncodedLabels' => [base64_encode('%PDF-1.4 test label')]]),
|
|
]);
|
|
config()->set('services.parcel2go', ['client_id' => 'id', 'client_secret' => 'secret', 'auth_url' => 'https://auth.example.test/token', 'base_url' => 'https://shipping.example.test/api']);
|
|
|
|
$this->actingAs(User::factory()->create(['role' => 'admin']))->get(route('shipping.label', 123))
|
|
->assertOk()->assertHeader('content-type', 'application/pdf')->assertSee('%PDF-1.4 test label');
|
|
Http::assertSent(fn ($request) => str_contains($request->url(), '/labels/123') && str_contains($request->url(), 'hash=order-hash'));
|
|
}
|
|
|
|
public function test_shipping_label_uses_the_hash_from_order_links_when_hash_field_is_empty(): void
|
|
{
|
|
Http::preventStrayRequests();
|
|
Http::fake([
|
|
'https://auth.example.test/token' => Http::response(['access_token' => 'test-token']),
|
|
'https://shipping.example.test/api/me/orders/detail' => Http::response(['Orders' => [[
|
|
'OrderLineId' => 123, 'Hash' => null,
|
|
'Links' => [['Name' => 'labels-a4', 'Link' => 'https://www.parcel2go.com/label/orderline?media=0&orderlineid=123&hash=link%2Bhash%3D']],
|
|
]]]),
|
|
'https://shipping.example.test/api/labels/123*' => Http::response(['Base64EncodedLabels' => [base64_encode('%PDF-1.4 test label')]]),
|
|
]);
|
|
config()->set('services.parcel2go', ['client_id' => 'id', 'client_secret' => 'secret', 'auth_url' => 'https://auth.example.test/token', 'base_url' => 'https://shipping.example.test/api']);
|
|
|
|
$admin = User::factory()->create(['role' => 'admin']);
|
|
$this->actingAs($admin)->get(route('shipping'))->assertSee(route('shipping.label', 123), false);
|
|
$this->actingAs($admin)->get(route('shipping.label', 123))->assertOk()->assertSee('%PDF-1.4 test label');
|
|
Http::assertSent(fn ($request) => str_contains($request->url(), '/labels/123') && str_contains($request->url(), 'hash=link%2Bhash%3D'));
|
|
}
|
|
|
|
public function test_shipping_label_can_be_requested_in_4x6_format(): void
|
|
{
|
|
Http::preventStrayRequests();
|
|
Http::fake([
|
|
'https://auth.example.test/token' => Http::response(['access_token' => 'test-token']),
|
|
'https://shipping.example.test/api/me/orders/detail' => Http::response(['Orders' => [['OrderLineId' => 123, 'Hash' => 'order-hash']]]),
|
|
'https://shipping.example.test/api/labels/123*' => Http::response(['Base64EncodedLabels' => [base64_encode('%PDF-1.4 test label')]]),
|
|
]);
|
|
config()->set('services.parcel2go', ['client_id' => 'id', 'client_secret' => 'secret', 'auth_url' => 'https://auth.example.test/token', 'base_url' => 'https://shipping.example.test/api']);
|
|
|
|
$this->actingAs(User::factory()->create(['role' => 'admin']))->get(route('shipping.label', [123, 'size' => '4x6']))
|
|
->assertOk()->assertHeader('content-disposition', 'attachment; filename="parcel2go-123-4x6.pdf"');
|
|
Http::assertSent(fn ($request) => str_contains($request->url(), '/labels/123') && str_contains($request->url(), 'labelMedia=Label4x6'));
|
|
}
|
|
|
|
public function test_collection_booking_uses_quoted_service_and_available_date(): void
|
|
{
|
|
$this->pendingEtsyOrder();
|
|
Http::preventStrayRequests();
|
|
Http::fake([
|
|
'https://auth.example.test/token' => Http::response(['access_token' => 'test-token']),
|
|
'https://shipping.example.test/api/orders' => Http::response(['OrderId' => '789', 'TotalPrice' => 8.5, 'Links' => ['payment' => 'https://www.parcel2go.com/order/payment?id=789']]),
|
|
'https://shipping.example.test/api/me/detail' => Http::response([]),
|
|
'https://shipping.example.test/api/me/prepay/balance' => Http::response([]),
|
|
'https://shipping.example.test/api/me/orders/detail' => Http::response(['Orders' => []]),
|
|
]);
|
|
config()->set('services.parcel2go', ['client_id' => 'id', 'client_secret' => 'secret', 'auth_url' => 'https://auth.example.test/token', 'base_url' => 'https://shipping.example.test/api']);
|
|
$session = [
|
|
'shipping_quote' => ['collection_postcode' => 'SW1A 1AA', 'delivery_postcode' => 'M1 1AE', 'weight' => 2, 'length' => 10, 'width' => 10, 'height' => 10, 'value' => 30],
|
|
'shipping_receipt_id' => 77,
|
|
'shipping_selected_service' => 'evri-collection',
|
|
'shipping_quotes' => [['Service' => ['Slug' => 'evri-collection', 'CollectionType' => 'Collection']]],
|
|
'shipping_collection_dates' => [['CollectionDate' => $collectionDate = now()->addDay()->utc()->format('Y-m-d\TH:i:s\Z')]],
|
|
'shipping_sender_location' => ['sender_street' => 'Main Street', 'sender_town' => 'London'],
|
|
];
|
|
$booking = [
|
|
'collection_date' => $collectionDate,
|
|
'sender_name' => 'Lee Hickman', 'sender_email' => 'lee@example.test', 'sender_phone' => '07123456789',
|
|
'sender_property' => '1', 'sender_street' => 'Main Street', 'sender_town' => 'London',
|
|
'recipient_name' => 'Buyer Name', 'recipient_email' => 'buyer@example.test', 'recipient_phone' => '07123456788',
|
|
'recipient_property' => '2', 'recipient_street' => 'Market Street', 'recipient_town' => 'Manchester', 'contents' => '3D printed part',
|
|
];
|
|
|
|
$this->actingAs(User::factory()->create(['role' => 'admin']))->withSession($session)
|
|
->post(route('shipping.book-collection'), $booking)->assertRedirect(route('shipping'))->assertSessionHas('shipping_booking_result.OrderId', '789');
|
|
$this->get(route('shipping'))->assertOk()->assertSee('Collection order #789')->assertSee('Complete payment');
|
|
Http::assertSent(fn ($request) => $request->url() === 'https://shipping.example.test/api/orders'
|
|
&& $request['Items'][0]['Service'] === 'evri-collection' && $request['Items'][0]['Parcels'][0]['ContentsSummary'] === '3D printed part');
|
|
}
|
|
|
|
public function test_collection_dates_only_accept_a_quoted_collection_service(): void
|
|
{
|
|
Http::preventStrayRequests();
|
|
$admin = User::factory()->create(['role' => 'admin']);
|
|
$session = [
|
|
'shipping_quote' => ['collection_postcode' => 'SW1A 1AA'],
|
|
'shipping_quotes' => [['Service' => ['Slug' => 'evri-collection', 'CollectionType' => 'Collection']]],
|
|
];
|
|
|
|
$this->actingAs($admin)->withSession($session)
|
|
->post(route('shipping.collection-dates'), ['service' => 'forged-service', 'sender_street' => 'Main Street', 'sender_town' => 'London'])
|
|
->assertSessionHasErrors('service');
|
|
Http::assertNothingSent();
|
|
}
|
|
|
|
public function test_collection_dates_are_loaded_for_a_quoted_service(): void
|
|
{
|
|
Http::preventStrayRequests();
|
|
Http::fake([
|
|
'https://auth.example.test/token' => Http::response(['access_token' => 'test-token']),
|
|
'https://shipping.example.test/api/collectiondates' => Http::response(['CollectionDates' => [['CollectionDate' => '2026-10-15T10:00:00Z']]]),
|
|
]);
|
|
config()->set('services.parcel2go', ['client_id' => 'id', 'client_secret' => 'secret', 'auth_url' => 'https://auth.example.test/token', 'base_url' => 'https://shipping.example.test/api']);
|
|
$session = [
|
|
'shipping_quote' => ['collection_postcode' => 'SW1A 1AA'],
|
|
'shipping_quotes' => [['Service' => ['Slug' => 'evri-collection', 'CollectionType' => 'Collection']]],
|
|
];
|
|
|
|
$this->actingAs(User::factory()->create(['role' => 'admin']))->withSession($session)
|
|
->post(route('shipping.collection-dates'), ['service' => 'evri-collection', 'sender_street' => 'Main Street', 'sender_town' => 'London'])
|
|
->assertRedirect(route('shipping'))->assertSessionHas('shipping_selected_service', 'evri-collection');
|
|
Http::assertSent(fn ($request) => $request->url() === 'https://shipping.example.test/api/collectiondates'
|
|
&& $request['ServiceSlug'] === 'evri-collection' && $request['Postcode'] === 'SW1A 1AA');
|
|
}
|
|
|
|
public function test_uncertain_booking_requires_account_check_before_a_new_quote(): void
|
|
{
|
|
Http::preventStrayRequests();
|
|
$admin = User::factory()->create(['role' => 'admin']);
|
|
$details = ['collection_postcode' => 'SW1A 1AA', 'delivery_postcode' => 'M1 1AE', 'weight' => 2, 'length' => 10, 'width' => 10, 'height' => 10, 'value' => 30];
|
|
|
|
$this->actingAs($admin)->withSession(['shipping_booking_pending' => true])
|
|
->post(route('shipping.quote'), $details)->assertSessionHasErrors('collection');
|
|
$this->actingAs($admin)->post(route('shipping.reset-booking'), [])->assertSessionHasErrors('checked_account');
|
|
$this->actingAs($admin)->post(route('shipping.reset-booking'), ['checked_account' => 1])
|
|
->assertRedirect(route('shipping'))->assertSessionMissing('shipping_booking_pending');
|
|
Http::assertNothingSent();
|
|
}
|
|
|
|
public function test_dryer_status_and_fault_acknowledgement_use_versioned_api(): void
|
|
{
|
|
Http::preventStrayRequests();
|
|
Http::fake([
|
|
'https://dryer.example.test/api/v1/status' => Http::response(['state' => 'idle', 'sample' => ['valid' => true, 'temperatureC' => 25]]),
|
|
'https://dryer.example.test/api/v1/history*' => Http::response(['samples' => []]),
|
|
'https://dryer.example.test/api/v1/faults/acknowledge' => Http::response(['ok' => true]),
|
|
]);
|
|
config()->set('services.dryer', ['url' => 'https://dryer.example.test/api/v1', 'api_key' => 'test-key']);
|
|
$admin = User::factory()->create(['role' => 'admin']);
|
|
|
|
$this->actingAs($admin)->get(route('dryer.status'))->assertOk()->assertJsonPath('status.state', 'idle');
|
|
$this->actingAs($admin)->postJson(route('dryer.acknowledge'))->assertOk()->assertJsonPath('ok', true);
|
|
Http::assertSent(fn ($request) => $request->url() === 'https://dryer.example.test/api/v1/faults/acknowledge'
|
|
&& $request->hasHeader('X-API-Key', 'test-key') && str_starts_with($request['requestId'], 'dashboard-'));
|
|
}
|
|
|
|
public function test_dryer_rejects_temperature_above_firmware_limit_without_contacting_device(): void
|
|
{
|
|
Http::preventStrayRequests();
|
|
$admin = User::factory()->create(['role' => 'admin']);
|
|
|
|
$this->actingAs($admin)->postJson(route('dryer.control'), [
|
|
'dryerOn' => true, 'profile' => 'PLA', 'targetTemperature' => 80, 'dryingTime' => 240,
|
|
])->assertUnprocessable()->assertJsonValidationErrors('targetTemperature');
|
|
Http::assertNothingSent();
|
|
}
|
|
|
|
private function pendingEtsyOrder(): void
|
|
{
|
|
config()->set('services.etsy', ['keystring' => 'key', 'shared_secret' => 'secret']);
|
|
EtsyConnection::create([
|
|
'shop_id' => 42, 'shop_name' => 'HickLab3D',
|
|
'access_token' => '123.token', 'refresh_token' => '123.refresh',
|
|
'expires_at' => now()->addHour(),
|
|
]);
|
|
IntegrationSetting::create([
|
|
'provider' => 'parcel2go',
|
|
'settings' => ['collection_postcode' => 'SW1A 1AA'],
|
|
]);
|
|
Http::fake([
|
|
'https://api.etsy.com/v3/application/shops/42/receipts*' => Http::response(['results' => [[
|
|
'receipt_id' => 77, 'name' => 'Buyer', 'zip' => 'M1 1AE', 'city' => 'Manchester', 'country_iso' => 'GB',
|
|
'grandtotal' => ['amount' => 3000, 'divisor' => 100, 'currency_code' => 'GBP'],
|
|
]]]),
|
|
]);
|
|
}
|
|
}
|