From 0b43cab8d9813d29418b1cbf35d4d729b7605ea2 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 26 May 2026 03:12:16 +0000 Subject: [PATCH 1/7] Prepare PHP SDK endpoint coverage release Co-authored-by: barnett --- lib/Trolley/BalanceGateway.php | 35 ++++++++++++- lib/Trolley/Batch.php | 8 ++- lib/Trolley/BatchGateway.php | 33 +++++++------ lib/Trolley/Configuration.php | 2 +- lib/Trolley/Gateway.php | 16 ++++++ lib/Trolley/InvoicePayment.php | 25 +++++++++- lib/Trolley/OfflinePayment.php | 8 ++- lib/Trolley/OfflinePaymentGateway.php | 8 +-- lib/Trolley/Payment.php | 38 +++++++++++++++ lib/Trolley/PaymentGateway.php | 12 +++++ lib/Trolley/Recipient.php | 4 +- lib/Trolley/RecipientAccount.php | 16 +++++- lib/Trolley/RecipientAccountGateway.php | 12 ++--- lib/Trolley/RecipientGateway.php | 27 ++++++++-- lib/Trolley/ResourceCollection.php | 9 +++- lib/Trolley/VerificationGateway.php | 65 +++++++++++++++++++++++++ lib/Trolley/Version.php | 2 +- 17 files changed, 278 insertions(+), 42 deletions(-) create mode 100644 lib/Trolley/VerificationGateway.php diff --git a/lib/Trolley/BalanceGateway.php b/lib/Trolley/BalanceGateway.php index 59a9b61..14b66b5 100644 --- a/lib/Trolley/BalanceGateway.php +++ b/lib/Trolley/BalanceGateway.php @@ -42,7 +42,8 @@ public function __construct($gateway) */ public function search($params, $query) { - $response = $this->_http->get('/v1/balances/'.$params, $query); + $path = $params ? "/v1/balances/{$params}" : '/v1/balances'; + $response = $this->_http->get($path, $query); if ($response['ok']) { @@ -63,6 +64,38 @@ public function search($params, $query) throw new Exception\DownForMaintenance(); } } + + public function all() + { + $response = $this->_http->get('/v1/balances'); + return $this->balancesCollection($response); + } + + public function paymentrails() + { + $response = $this->_http->get('/v1/balances/paymentrails'); + return $this->balancesCollection($response); + } + + public function paypal() + { + $response = $this->_http->get('/v1/balances/paypal'); + return $this->balancesCollection($response); + } + + private function balancesCollection($response) + { + if ($response['ok']) { + $items = array_map(function ($item) { + return Balance::factory($item); + }, $response['balances']); + return new ResourceCollection($response, $items, []); + } else if ($response['errors']) { + throw new Exception\Standard($response['errors']); + } else { + throw new Exception\DownForMaintenance(); + } + } } class_alias('Trolley\BalanceGateway', 'Trolley_BalanceGateway'); diff --git a/lib/Trolley/Batch.php b/lib/Trolley/Batch.php index 5189309..336cfc5 100644 --- a/lib/Trolley/Batch.php +++ b/lib/Trolley/Batch.php @@ -26,7 +26,9 @@ class Batch extends Base "status" => "", "totalPayments" => "", "updatedAt" => "", - "payments" => "" + "payments" => "", + "quoteExpiredAt" => "", + "tags" => "" ]; /** @@ -240,7 +242,9 @@ protected function _initialize($attributes) { "status", "totalPayments", "updatedAt", - "payments" => 'Trolley\Payment::factoryArray' + "payments" => 'Trolley\Payment::factoryArray', + "quoteExpiredAt", + "tags" ]; foreach ($fields as $key => $field) { diff --git a/lib/Trolley/BatchGateway.php b/lib/Trolley/BatchGateway.php index 02473a8..1934d4b 100644 --- a/lib/Trolley/BatchGateway.php +++ b/lib/Trolley/BatchGateway.php @@ -67,7 +67,7 @@ public function search($query) * Fetch a Batch by ID */ public function find($id) { - $response = $this->_http->get('/v1/batches/' . $id, null); + $response = $this->_http->get("/v1/batches/{$id}", null); if ($response['ok']) { return Batch::factory($response['batch']); @@ -92,7 +92,7 @@ public function create($attrib, $payments = []) { } public function update($batchId, $attrib) { - $response = $this->_http->patch('/v1/batches/' . $batchId, $attrib); + $response = $this->_http->patch("/v1/batches/{$batchId}", $attrib); if ($response['ok']) { return true; } else if ($response['errors']){ @@ -103,7 +103,7 @@ public function update($batchId, $attrib) { } public function delete($batchId) { - $response = $this->_http->delete('/v1/batches/' . $batchId); + $response = $this->_http->delete("/v1/batches/{$batchId}"); if ($response['ok']) { return true; } else if ($response['errors']){ @@ -125,7 +125,7 @@ public function deleteMultiple($batchIds) { } public function summary($batchId) { - $response = $this->_http->get('/v1/batches/' . $batchId . '/summary'); + $response = $this->_http->get("/v1/batches/{$batchId}/summary"); if ($response['ok']) { return BatchSummary::factory($response['batchSummary']); } else if ($response['errors']){ @@ -136,7 +136,7 @@ public function summary($batchId) { } public function generateQuote($batchId) { - $response = $this->_http->post('/v1/batches/' . $batchId . '/generate-quote'); + $response = $this->_http->post("/v1/batches/{$batchId}/generate-quote"); if ($response['ok']) { return true; } else if ($response['errors']){ @@ -147,7 +147,7 @@ public function generateQuote($batchId) { } public function startProcessing($batchId) { - $response = $this->_http->post('/v1/batches/' . $batchId . '/start-processing'); + $response = $this->_http->post("/v1/batches/{$batchId}/start-processing"); if ($response['ok']) { return true; } else if ($response['errors']){ @@ -158,7 +158,7 @@ public function startProcessing($batchId) { } public function createPayment($batchId, $payment) { - $response = $this->_http->post('/v1/batches/' . $batchId . '/payments', $payment); + $response = $this->_http->post("/v1/batches/{$batchId}/payments", $payment); if ($response['ok']) { return Payment::factory($response['payment']); } else if ($response['errors']){ @@ -169,7 +169,7 @@ public function createPayment($batchId, $payment) { } public function findPayment($batchId, $paymentId) { - $response = $this->_http->get('/v1/batches/' . $batchId . '/payments/' . $paymentId); + $response = $this->_http->get("/v1/batches/{$batchId}/payments/{$paymentId}"); if ($response['ok']) { return Payment::factory($response['payment']); } else if ($response['errors']){ @@ -180,7 +180,7 @@ public function findPayment($batchId, $paymentId) { } public function updatePayment($batchId, $paymentId, $params) { - $response = $this->_http->patch('/v1/batches/' . $batchId . '/payments/' . $paymentId, $params); + $response = $this->_http->patch("/v1/batches/{$batchId}/payments/{$paymentId}", $params); if ($response['ok']) { return true; } else if ($response['errors']){ @@ -191,7 +191,7 @@ public function updatePayment($batchId, $paymentId, $params) { } public function deletePayment($batchId, $paymentId) { - $response = $this->_http->delete('/v1/batches/' . $batchId . '/payments/' . $paymentId); + $response = $this->_http->delete("/v1/batches/{$batchId}/payments/{$paymentId}"); if ($response['ok']) { return true; } else if ($response['errors']){ @@ -202,18 +202,21 @@ public function deletePayment($batchId, $paymentId) { } public function payments($batchId, $params = []) { - return $this->paymentsInternal( - array_merge(['batchId' => $batchId], $params) - ); + $response = $this->_http->get("/v1/batches/{$batchId}/payments", $params); + return $this->paymentsCollection($response, $batchId, $params); } public function paymentsInternal($params) { - $response = $this->_http->get('/v1/batches/' . $params['batchId'] . '/payments', $params); + $response = $this->_http->get("/v1/batches/{$params['batchId']}/payments", $params); + return $this->paymentsCollection($response, $params['batchId'], $params); + } + + private function paymentsCollection($response, $batchId, $params) { if ($response['ok']) { $pager = [ 'object' => $this, 'method' => 'paymentsInternal', - 'methodArgs' => $params, + 'methodArgs' => array_merge(['batchId' => $batchId], $params), ]; $items = array_map(function ($item) { diff --git a/lib/Trolley/Configuration.php b/lib/Trolley/Configuration.php index 6d5cb39..e408c9b 100644 --- a/lib/Trolley/Configuration.php +++ b/lib/Trolley/Configuration.php @@ -13,7 +13,7 @@ class Configuration { public static $global; - private $_environment = null; + private $_environment = "production"; private $_merchantId = null; private $_publicKey = null; private $_privateKey = null; diff --git a/lib/Trolley/Gateway.php b/lib/Trolley/Gateway.php index 5186ffd..c35b9dd 100644 --- a/lib/Trolley/Gateway.php +++ b/lib/Trolley/Gateway.php @@ -94,6 +94,22 @@ public function balance() { return new BalanceGateway($this); } + + /** + * @return VerificationGateway + */ + public function verification() + { + return new VerificationGateway($this); + } + + /** + * @return VerificationGateway + */ + public function trust() + { + return $this->verification(); + } } class_alias('Trolley\Gateway', 'Trolley_Gateway'); diff --git a/lib/Trolley/InvoicePayment.php b/lib/Trolley/InvoicePayment.php index ec33529..bed236f 100644 --- a/lib/Trolley/InvoicePayment.php +++ b/lib/Trolley/InvoicePayment.php @@ -19,7 +19,21 @@ class InvoicePayment extends Base "invoiceId" => "", "invoiceLineId" => "", "paymentId" => "", - "amount" => "" + "amount", + "batchId", + "invoicePayments", + "status", + "memo", + "externalId", + "tags", + "coverFees" => "", + "batchId" => "", + "invoicePayments" => "", + "status" => "", + "memo" => "", + "externalId" => "", + "tags" => "", + "coverFees" => "" ]; /** @@ -95,7 +109,14 @@ protected function _initialize($attributes) { "invoiceId", "invoiceLineId", "paymentId", - "amount" + "amount", + "batchId", + "invoicePayments", + "status", + "memo", + "externalId", + "tags", + "coverFees" ]; foreach ($fields as $key => $field) { diff --git a/lib/Trolley/OfflinePayment.php b/lib/Trolley/OfflinePayment.php index 7fcc5ed..af9b3b5 100644 --- a/lib/Trolley/OfflinePayment.php +++ b/lib/Trolley/OfflinePayment.php @@ -32,7 +32,11 @@ class OfflinePayment extends Base "enteredAmount" => "", "updatedAt" => "", "createdAt" => "", - "deletedAt" => "", + "deletedAt", + "activityCount", + "taxReportable" => "", + "activityCount" => "", + "taxReportable" => "", ]; /** @@ -132,6 +136,8 @@ protected function _initialize($attributes) { "updatedAt", "createdAt", "deletedAt", + "activityCount", + "taxReportable", ]; foreach ($fields as $field) { diff --git a/lib/Trolley/OfflinePaymentGateway.php b/lib/Trolley/OfflinePaymentGateway.php index 9a70cd2..dd636ed 100644 --- a/lib/Trolley/OfflinePaymentGateway.php +++ b/lib/Trolley/OfflinePaymentGateway.php @@ -45,7 +45,7 @@ public function __construct($gateway) public function search($query) { if (isset($query["recipientId"])) { - $response = $this->_http->get('/v1/recipients/'.$query["recipientId"].'/offlinePayments', $query); + $response = $this->_http->get("/v1/recipients/{$query['recipientId']}/offlinePayments", $query); } else { $response = $this->_http->get('/v1/offline-payments', $query); } @@ -70,7 +70,7 @@ public function search($query) } public function create($recipientId, $offlinePaymentBody) { - $response = $this->_http->post('/v1/recipients/' . $recipientId . '/offlinePayments', $offlinePaymentBody); + $response = $this->_http->post("/v1/recipients/{$recipientId}/offlinePayments", $offlinePaymentBody); if ($response['ok']) { return OfflinePayment::factory($response['offlinePayment']); } else if ($response['errors']){ @@ -81,7 +81,7 @@ public function create($recipientId, $offlinePaymentBody) { } public function update($recipientId, $offlinePaymentId, $offlinePaymentBody) { - $response = $this->_http->patch('/v1/recipients/' . $recipientId . '/offlinePayments/' . $offlinePaymentId, $offlinePaymentBody); + $response = $this->_http->patch("/v1/recipients/{$recipientId}/offlinePayments/{$offlinePaymentId}", $offlinePaymentBody); if ($response['ok']) { return true; } else if ($response['errors']){ @@ -92,7 +92,7 @@ public function update($recipientId, $offlinePaymentId, $offlinePaymentBody) { } public function delete($recipientId, $offlinePaymentId) { - $response = $this->_http->delete('/v1/recipients/' . $recipientId . '/offlinePayments/' . $offlinePaymentId); + $response = $this->_http->delete("/v1/recipients/{$recipientId}/offlinePayments/{$offlinePaymentId}"); if ($response['ok']) { return true; } else if ($response['errors']){ diff --git a/lib/Trolley/Payment.php b/lib/Trolley/Payment.php index 6de3ec0..ad08b0f 100644 --- a/lib/Trolley/Payment.php +++ b/lib/Trolley/Payment.php @@ -19,15 +19,21 @@ class Payment extends Base 'id', 'methodDisplay', 'recipient', + 'batch', + 'batch', 'status', 'isSupplyPayment', 'returnedAmount', 'amount', 'currency', + 'category', + 'category', 'sourceAmount', 'sourceCurrency', + 'sourceCurrencyName', 'targetAmount', 'targetCurrency', + 'targetCurrencyName', 'exchangeRate', 'fees', 'recipientFees', @@ -45,6 +51,19 @@ class Payment extends Base 'checkNumber', 'tags', 'estimatedDeliveryAt', + 'equivalentWithholdingAmount', + 'equivalentWithholdingCurrency', + 'failureMessage', + 'merchantId', + 'returnedAt', + 'returnedNote', + 'returnedReason', + 'settledAt', + 'taxBasisAmount', + 'taxBasisCurrency', + 'withholdingAmount', + 'withholdingCurrency', + 'visibleToRecipient', 'initiatedAt', 'returnedAt', ]; @@ -85,15 +104,21 @@ protected function _initialize($attributes) { 'id', 'methodDisplay', 'recipient', + 'batch', + 'batch', 'status', 'isSupplyPayment', 'returnedAmount', 'amount', 'currency', + 'category', + 'category', 'sourceAmount', 'sourceCurrency', + 'sourceCurrencyName', 'targetAmount', 'targetCurrency', + 'targetCurrencyName', 'exchangeRate', 'fees', 'recipientFees', @@ -111,6 +136,19 @@ protected function _initialize($attributes) { 'checkNumber', 'tags', 'estimatedDeliveryAt', + 'equivalentWithholdingAmount', + 'equivalentWithholdingCurrency', + 'failureMessage', + 'merchantId', + 'returnedAt', + 'returnedNote', + 'returnedReason', + 'settledAt', + 'taxBasisAmount', + 'taxBasisCurrency', + 'withholdingAmount', + 'withholdingCurrency', + 'visibleToRecipient', 'initiatedAt', 'returnedAt', ]; diff --git a/lib/Trolley/PaymentGateway.php b/lib/Trolley/PaymentGateway.php index 26b7744..8d147d8 100644 --- a/lib/Trolley/PaymentGateway.php +++ b/lib/Trolley/PaymentGateway.php @@ -65,6 +65,18 @@ public function search($id, $query) } } + public function find($paymentId) + { + $response = $this->_http->get("/v1/payments/{$paymentId}"); + if ($response['ok']) { + return Payment::factory($response['payment']); + } else if ($response['errors']){ + throw new Exception\Standard($response['errors']); + } else { + throw new Exception\DownForMaintenance(); + } + } + /** * generic method for validating incoming gateway responses * diff --git a/lib/Trolley/Recipient.php b/lib/Trolley/Recipient.php index 0dbdea4..4c471d0 100644 --- a/lib/Trolley/Recipient.php +++ b/lib/Trolley/Recipient.php @@ -43,6 +43,7 @@ class Recipient extends Base "payoutMethod" => "", "compliance" => "", "accounts" => "", + "tags" => "", "address" => "", ]; @@ -169,7 +170,8 @@ protected function _initialize($attributes) { "merchantId", "payoutMethod", "compliance", - "accounts" => 'Trolley\RecipientAccount::factoryArray', // Specifies factory method + "accounts" => 'Trolley\RecipientAccount::factoryArray', + "tags", // Specifies factory method "address" => 'Trolley\RecipientAddress::factory', ]; diff --git a/lib/Trolley/RecipientAccount.php b/lib/Trolley/RecipientAccount.php index d581848..cf27a7b 100644 --- a/lib/Trolley/RecipientAccount.php +++ b/lib/Trolley/RecipientAccount.php @@ -39,7 +39,15 @@ class RecipientAccount extends Base "bankRegionCode" => "", "bankPostalCode" => "", "routeType" => "", - "recipientFees" => "" + "recipientFees", + "emailAddress", + "cardDetails", + "mailing", + "phoneNumber" => "", + "emailAddress" => "", + "cardDetails" => "", + "mailing" => "", + "phoneNumber" => "" ]; /** @@ -130,7 +138,11 @@ protected function _initialize($attributes) { "bankRegionCode", "bankPostalCode", "routeType", - "recipientFees" + "recipientFees", + "emailAddress", + "cardDetails", + "mailing", + "phoneNumber" ]; foreach ($fields as $field) { diff --git a/lib/Trolley/RecipientAccountGateway.php b/lib/Trolley/RecipientAccountGateway.php index 435baaf..7d48eb2 100644 --- a/lib/Trolley/RecipientAccountGateway.php +++ b/lib/Trolley/RecipientAccountGateway.php @@ -43,7 +43,7 @@ public function __construct($gateway) */ public function all($recipientId) { - $response = $this->_http->get('/v1/recipients/' . $recipientId . '/accounts'); + $response = $this->_http->get("/v1/recipients/{$recipientId}/accounts"); if ($response['ok']) { return array_map(function ($item) { @@ -60,7 +60,7 @@ public function all($recipientId) * Fetch a recipient by ID */ public function find($recipientId, $accountId) { - $response = $this->_http->get('/v1/recipients/' . $recipientId . '/accounts/' . $accountId); + $response = $this->_http->get("/v1/recipients/{$recipientId}/accounts/{$accountId}"); if ($response['ok']) { return RecipientAccount::factory($response['account']); @@ -72,7 +72,7 @@ public function find($recipientId, $accountId) { } public function create($recipientId, $attrib) { - $response = $this->_http->post('/v1/recipients/' . $recipientId . '/accounts', $attrib); + $response = $this->_http->post("/v1/recipients/{$recipientId}/accounts", $attrib); if ($response['ok']) { return RecipientAccount::factory($response['account']); } else if ($response['errors']){ @@ -83,9 +83,9 @@ public function create($recipientId, $attrib) { } public function update($recipientId, $accountId, $attrib) { - $response = $this->_http->patch('/v1/recipients/' . $recipientId . '/accounts/' . $accountId, $attrib); + $response = $this->_http->patch("/v1/recipients/{$recipientId}/accounts/{$accountId}", $attrib); if ($response['ok']) { - return Recipient::factory($response['account']); + return RecipientAccount::factory($response['account']); } else if ($response['errors']){ throw new Exception\Standard($response['errors']); } else { @@ -94,7 +94,7 @@ public function update($recipientId, $accountId, $attrib) { } public function delete($recipientId, $accountId) { - $response = $this->_http->delete('/v1/recipients/' . $recipientId . '/accounts/' . $accountId); + $response = $this->_http->delete("/v1/recipients/{$recipientId}/accounts/{$accountId}"); if ($response['ok']) { return true; } else if ($response['errors']){ diff --git a/lib/Trolley/RecipientGateway.php b/lib/Trolley/RecipientGateway.php index 0d61ffe..6797552 100644 --- a/lib/Trolley/RecipientGateway.php +++ b/lib/Trolley/RecipientGateway.php @@ -69,7 +69,7 @@ public function search($query) * Fetch a recipient by ID */ public function find($id) { - $response = $this->_http->get('/v1/recipients/' . $id, null); + $response = $this->_http->get("/v1/recipients/{$id}", null); if ($response['ok']) { return Recipient::factory($response['recipient']); @@ -92,7 +92,7 @@ public function create($attrib) { } public function update($id, $attrib) { - $response = $this->_http->patch('/v1/recipients/' . $id, $attrib); + $response = $this->_http->patch("/v1/recipients/{$id}", $attrib); if ($response['ok']) { return true; } else if ($response['errors']){ @@ -103,7 +103,7 @@ public function update($id, $attrib) { } public function delete($id) { - $response = $this->_http->delete('/v1/recipients/' . $id); + $response = $this->_http->delete("/v1/recipients/{$id}"); if ($response) { return true; } else { @@ -153,7 +153,7 @@ public function getAllLogs($id) { */ public function getAllPayments($recipientId) { - $response = $this->_http->get('/v1/recipients/'.$recipientId.'/payments'); + $response = $this->_http->get("/v1/recipients/{$recipientId}/payments"); if ($response['ok']) { $pager = [ @@ -173,6 +173,25 @@ public function getAllPayments($recipientId) throw new Exception\DownForMaintenance(); } } + + public function getAllOfflinePayments($recipientId, $query = []) + { + $response = $this->_http->get("/v1/recipients/{$recipientId}/offlinePayments", $query); + if ($response['ok']) { + $items = array_map(function ($item) { + return OfflinePayment::factory($item); + }, $response['offlinePayments']); + return new ResourceCollection($response, $items, [ + 'object' => $this, + 'method' => 'getAllOfflinePayments', + 'methodArgs' => $recipientId + ]); + } else if ($response['errors']){ + throw new Exception\Standard($response['errors']); + } else { + throw new Exception\DownForMaintenance(); + } + } } class_alias('Trolley\RecipientGateway', 'Trolley_RecipientGateway'); diff --git a/lib/Trolley/ResourceCollection.php b/lib/Trolley/ResourceCollection.php index 946f837..b92ef58 100644 --- a/lib/Trolley/ResourceCollection.php +++ b/lib/Trolley/ResourceCollection.php @@ -56,9 +56,10 @@ public function __construct($response, $items, $pager) /** * returns the current item when iterating with foreach */ + #[\ReturnTypeWillChange] public function current() { - return $this->_items[$this->_index]; + return isset($this->_items[$this->_index]) ? $this->_items[$this->_index] : false; } /** @@ -68,9 +69,10 @@ public function current() */ public function firstItem() { - return $this->_items[0]; + return isset($this->_items[0]) ? $this->_items[0] : false; } + #[\ReturnTypeWillChange] public function key() { return null; @@ -79,6 +81,7 @@ public function key() /** * advances to the next item in the collection when iterating with foreach */ + #[\ReturnTypeWillChange] public function next() { ++$this->_index; @@ -87,6 +90,7 @@ public function next() /** * rewinds the testIterateOverResults collection to the first item when iterating with foreach */ + #[\ReturnTypeWillChange] public function rewind() { $this->_index = 0; @@ -95,6 +99,7 @@ public function rewind() /** * returns whether the current item is valid when iterating with foreach */ + #[\ReturnTypeWillChange] public function valid() { if ($this->_index >= count($this->_items)) { diff --git a/lib/Trolley/VerificationGateway.php b/lib/Trolley/VerificationGateway.php new file mode 100644 index 0000000..c802c68 --- /dev/null +++ b/lib/Trolley/VerificationGateway.php @@ -0,0 +1,65 @@ +_gateway = $gateway; + $this->_config = $gateway->config; + $this->_config->assertHasAccessTokenOrKeys(); + $this->_http = new Http($gateway->config); + } + + public function search($query = []) + { + $response = $this->_http->get('/v1/verifications', $query); + return $this->buildCollection($response); + } + + public function expire($body) + { + $response = $this->_http->patch('/v1/verifications/expire', $body); + return $this->buildCollection($response); + } + + public function trigger($verificationType, $body) + { + $response = $this->_http->post("/v1/verifications/{$verificationType}/trigger", $body); + return $this->buildCollection($response); + } + + public function triggerWatchlist($body) + { + $response = $this->_http->post('/v1/verifications/watchlist/trigger', $body); + return $this->buildCollection($response); + } + + public function trigger_watchlist($body) + { + return $this->triggerWatchlist($body); + } + + private function buildCollection($response) + { + if ($response['ok']) { + $pager = [ + 'object' => $this, + 'method' => 'search', + 'methodArgs' => [] + ]; + + return new ResourceCollection($response, $response['verifications'], $pager); + } else if ($response['errors']) { + throw new Exception\Standard($response['errors']); + } else { + throw new Exception\DownForMaintenance(); + } + } +} + +class_alias('Trolley\VerificationGateway', 'Trolley_VerificationGateway'); diff --git a/lib/Trolley/Version.php b/lib/Trolley/Version.php index c2db000..c6c15c4 100644 --- a/lib/Trolley/Version.php +++ b/lib/Trolley/Version.php @@ -12,7 +12,7 @@ class Version */ const MAJOR = 3; const MINOR = 0; - const TINY = 2; + const TINY = 3; /** * @ignore From d215fe9b9164b90e3d252194110692f07e048f50 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 26 May 2026 04:28:19 +0000 Subject: [PATCH 2/7] Bump PHP SDK minor version Co-authored-by: barnett --- lib/Trolley/Version.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Trolley/Version.php b/lib/Trolley/Version.php index c6c15c4..2f2963a 100644 --- a/lib/Trolley/Version.php +++ b/lib/Trolley/Version.php @@ -11,8 +11,8 @@ class Version * class constants */ const MAJOR = 3; - const MINOR = 0; - const TINY = 3; + const MINOR = 1; + const TINY = 0; /** * @ignore From 5c833d7abc86518bf80203c5d57c1f2f62bdf5b7 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 02:40:05 +0000 Subject: [PATCH 3/7] Fix PR review findings Co-authored-by: barnett --- lib/Trolley/BalanceGateway.php | 39 ++++++--- lib/Trolley/InvoicePayment.php | 9 +- lib/Trolley/OfflinePayment.php | 4 +- lib/Trolley/Payment.php | 35 ++++---- lib/Trolley/RecipientAccount.php | 6 +- lib/Trolley/RecipientGateway.php | 11 ++- tests/unit/ReviewCommentTest.php | 136 +++++++++++++++++++++++++++++++ 7 files changed, 189 insertions(+), 51 deletions(-) create mode 100644 tests/unit/ReviewCommentTest.php diff --git a/lib/Trolley/BalanceGateway.php b/lib/Trolley/BalanceGateway.php index 14b66b5..488a47b 100644 --- a/lib/Trolley/BalanceGateway.php +++ b/lib/Trolley/BalanceGateway.php @@ -49,8 +49,8 @@ public function search($params, $query) if ($response['ok']) { $pager = [ 'object' => $this, - 'method' => 'search', - 'methodArgs' => $query + 'method' => 'searchPage', + 'methodArgs' => array_merge(['params' => $params], $query) ]; $items = array_map(function ($item) { @@ -65,31 +65,44 @@ public function search($params, $query) } } - public function all() + public function searchPage($query) { - $response = $this->_http->get('/v1/balances'); - return $this->balancesCollection($response); + $params = isset($query['params']) ? $query['params'] : ''; + unset($query['params']); + return $this->search($params, $query); } - public function paymentrails() + public function all($query = []) { - $response = $this->_http->get('/v1/balances/paymentrails'); - return $this->balancesCollection($response); + $response = $this->_http->get('/v1/balances', $query); + return $this->balancesCollection($response, 'all', $query); } - public function paypal() + public function paymentrails($query = []) { - $response = $this->_http->get('/v1/balances/paypal'); - return $this->balancesCollection($response); + $response = $this->_http->get('/v1/balances/paymentrails', $query); + return $this->balancesCollection($response, 'paymentrails', $query); } - private function balancesCollection($response) + public function paypal($query = []) + { + $response = $this->_http->get('/v1/balances/paypal', $query); + return $this->balancesCollection($response, 'paypal', $query); + } + + private function balancesCollection($response, $method, $query) { if ($response['ok']) { + $pager = [ + 'object' => $this, + 'method' => $method, + 'methodArgs' => $query + ]; + $items = array_map(function ($item) { return Balance::factory($item); }, $response['balances']); - return new ResourceCollection($response, $items, []); + return new ResourceCollection($response, $items, $pager); } else if ($response['errors']) { throw new Exception\Standard($response['errors']); } else { diff --git a/lib/Trolley/InvoicePayment.php b/lib/Trolley/InvoicePayment.php index bed236f..2167342 100644 --- a/lib/Trolley/InvoicePayment.php +++ b/lib/Trolley/InvoicePayment.php @@ -19,14 +19,7 @@ class InvoicePayment extends Base "invoiceId" => "", "invoiceLineId" => "", "paymentId" => "", - "amount", - "batchId", - "invoicePayments", - "status", - "memo", - "externalId", - "tags", - "coverFees" => "", + "amount" => "", "batchId" => "", "invoicePayments" => "", "status" => "", diff --git a/lib/Trolley/OfflinePayment.php b/lib/Trolley/OfflinePayment.php index af9b3b5..4b448f7 100644 --- a/lib/Trolley/OfflinePayment.php +++ b/lib/Trolley/OfflinePayment.php @@ -32,9 +32,7 @@ class OfflinePayment extends Base "enteredAmount" => "", "updatedAt" => "", "createdAt" => "", - "deletedAt", - "activityCount", - "taxReportable" => "", + "deletedAt" => "", "activityCount" => "", "taxReportable" => "", ]; diff --git a/lib/Trolley/Payment.php b/lib/Trolley/Payment.php index ad08b0f..80cf152 100644 --- a/lib/Trolley/Payment.php +++ b/lib/Trolley/Payment.php @@ -19,14 +19,12 @@ class Payment extends Base 'id', 'methodDisplay', 'recipient', - 'batch', 'batch', 'status', 'isSupplyPayment', 'returnedAmount', 'amount', 'currency', - 'category', 'category', 'sourceAmount', 'sourceCurrency', @@ -105,20 +103,18 @@ protected function _initialize($attributes) { 'methodDisplay', 'recipient', 'batch', - 'batch', 'status', 'isSupplyPayment', 'returnedAmount', 'amount', 'currency', 'category', - 'category', 'sourceAmount', 'sourceCurrency', - 'sourceCurrencyName', + 'sourceCurrencyName', 'targetAmount', 'targetCurrency', - 'targetCurrencyName', + 'targetCurrencyName', 'exchangeRate', 'fees', 'recipientFees', @@ -136,21 +132,20 @@ protected function _initialize($attributes) { 'checkNumber', 'tags', 'estimatedDeliveryAt', - 'equivalentWithholdingAmount', - 'equivalentWithholdingCurrency', - 'failureMessage', - 'merchantId', - 'returnedAt', - 'returnedNote', - 'returnedReason', - 'settledAt', - 'taxBasisAmount', - 'taxBasisCurrency', - 'withholdingAmount', - 'withholdingCurrency', - 'visibleToRecipient', - 'initiatedAt', + 'equivalentWithholdingAmount', + 'equivalentWithholdingCurrency', + 'failureMessage', + 'merchantId', 'returnedAt', + 'returnedNote', + 'returnedReason', + 'settledAt', + 'taxBasisAmount', + 'taxBasisCurrency', + 'withholdingAmount', + 'withholdingCurrency', + 'visibleToRecipient', + 'initiatedAt', ]; foreach ($fields as $field) { diff --git a/lib/Trolley/RecipientAccount.php b/lib/Trolley/RecipientAccount.php index cf27a7b..28e8549 100644 --- a/lib/Trolley/RecipientAccount.php +++ b/lib/Trolley/RecipientAccount.php @@ -39,11 +39,7 @@ class RecipientAccount extends Base "bankRegionCode" => "", "bankPostalCode" => "", "routeType" => "", - "recipientFees", - "emailAddress", - "cardDetails", - "mailing", - "phoneNumber" => "", + "recipientFees" => "", "emailAddress" => "", "cardDetails" => "", "mailing" => "", diff --git a/lib/Trolley/RecipientGateway.php b/lib/Trolley/RecipientGateway.php index 6797552..02a6b83 100644 --- a/lib/Trolley/RecipientGateway.php +++ b/lib/Trolley/RecipientGateway.php @@ -183,8 +183,8 @@ public function getAllOfflinePayments($recipientId, $query = []) }, $response['offlinePayments']); return new ResourceCollection($response, $items, [ 'object' => $this, - 'method' => 'getAllOfflinePayments', - 'methodArgs' => $recipientId + 'method' => 'getAllOfflinePaymentsPage', + 'methodArgs' => array_merge(['recipientId' => $recipientId], $query) ]); } else if ($response['errors']){ throw new Exception\Standard($response['errors']); @@ -192,6 +192,13 @@ public function getAllOfflinePayments($recipientId, $query = []) throw new Exception\DownForMaintenance(); } } + + public function getAllOfflinePaymentsPage($query) + { + $recipientId = $query['recipientId']; + unset($query['recipientId']); + return $this->getAllOfflinePayments($recipientId, $query); + } } class_alias('Trolley\RecipientGateway', 'Trolley_RecipientGateway'); diff --git a/tests/unit/ReviewCommentTest.php b/tests/unit/ReviewCommentTest.php new file mode 100644 index 0000000..db3830f --- /dev/null +++ b/tests/unit/ReviewCommentTest.php @@ -0,0 +1,136 @@ +assertSame('', InvoicePayment::factory([])->amount); + $this->assertSame('', OfflinePayment::factory([])->deletedAt); + $this->assertSame('', RecipientAccount::factory([])->recipientFees); + } + + public function testPaymentAttributesAreUnique() + { + $attributes = $this->attributesFor(Payment::factory([])); + + $this->assertSame(count($attributes), count(array_unique($attributes))); + } + + public function testRecipientOfflinePaymentsPaginationKeepsRecipientId() + { + $http = new FakeHttp([ + [ + 'ok' => true, + 'offlinePayments' => [['id' => 'OP-1']], + 'meta' => ['page' => 1, 'pages' => 3, 'records' => 2], + ], + [ + 'ok' => true, + 'offlinePayments' => [['id' => 'OP-2']], + 'meta' => ['page' => 2, 'pages' => 3, 'records' => 2], + ], + ]); + $gateway = $this->gatewayWithHttp('Trolley\RecipientGateway', $http); + + $ids = []; + foreach ($gateway->getAllOfflinePayments('R-1', ['pageSize' => 1]) as $payment) { + $ids[] = $payment->id; + } + + $this->assertSame(['OP-1', 'OP-2'], $ids); + $this->assertSame('/v1/recipients/R-1/offlinePayments', $http->requests[1][0]); + $this->assertSame(['page' => 2, 'pageSize' => 1], $http->requests[1][1]); + } + + public function testBalancePaginationHasPager() + { + $http = new FakeHttp([ + [ + 'ok' => true, + 'balances' => [['accountNumber' => 'A-1']], + 'meta' => ['page' => 1, 'pages' => 3, 'records' => 2], + ], + [ + 'ok' => true, + 'balances' => [['accountNumber' => 'A-2']], + 'meta' => ['page' => 2, 'pages' => 3, 'records' => 2], + ], + ]); + $gateway = $this->gatewayWithHttp('Trolley\BalanceGateway', $http); + + $accountNumbers = []; + foreach ($gateway->all(['pageSize' => 1]) as $balance) { + $accountNumbers[] = $balance->accountNumber; + } + + $this->assertSame(['A-1', 'A-2'], $accountNumbers); + $this->assertSame('/v1/balances', $http->requests[1][0]); + $this->assertSame(['page' => 2, 'pageSize' => 1], $http->requests[1][1]); + } + + public function testBalanceSearchPaginationKeepsPathSegment() + { + $http = new FakeHttp([ + [ + 'ok' => true, + 'balances' => [['accountNumber' => 'A-1']], + 'meta' => ['page' => 1, 'pages' => 3, 'records' => 2], + ], + [ + 'ok' => true, + 'balances' => [['accountNumber' => 'A-2']], + 'meta' => ['page' => 2, 'pages' => 3, 'records' => 2], + ], + ]); + $gateway = $this->gatewayWithHttp('Trolley\BalanceGateway', $http); + + $accountNumbers = []; + foreach ($gateway->search('paypal', ['pageSize' => 1]) as $balance) { + $accountNumbers[] = $balance->accountNumber; + } + + $this->assertSame(['A-1', 'A-2'], $accountNumbers); + $this->assertSame('/v1/balances/paypal', $http->requests[1][0]); + $this->assertSame(['page' => 2, 'pageSize' => 1], $http->requests[1][1]); + } + + private function attributesFor($model) + { + $property = new ReflectionProperty($model, '_attributes'); + $property->setAccessible(true); + return $property->getValue($model); + } + + private function gatewayWithHttp($className, $http) + { + $reflection = new ReflectionClass($className); + $gateway = $reflection->newInstanceWithoutConstructor(); + $property = new ReflectionProperty($className, '_http'); + $property->setAccessible(true); + $property->setValue($gateway, $http); + return $gateway; + } +} + +class FakeHttp +{ + public $requests = []; + private $responses; + + public function __construct($responses) + { + $this->responses = $responses; + } + + public function get($path, $query = null) + { + $this->requests[] = [$path, $query]; + return array_shift($this->responses); + } +} From 3dcd5def7d77013eadb08c7e0125d67e420d720c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 02:42:07 +0000 Subject: [PATCH 4/7] Remove remaining payment attribute duplicate Co-authored-by: barnett --- lib/Trolley/Payment.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Trolley/Payment.php b/lib/Trolley/Payment.php index 80cf152..d789568 100644 --- a/lib/Trolley/Payment.php +++ b/lib/Trolley/Payment.php @@ -63,7 +63,6 @@ class Payment extends Base 'withholdingCurrency', 'visibleToRecipient', 'initiatedAt', - 'returnedAt', ]; /** From 9b036290500975a2932e6af5da20bce73f4e2b44 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 04:05:59 +0000 Subject: [PATCH 5/7] Preserve verification search pagination filters Co-authored-by: barnett --- lib/Trolley/VerificationGateway.php | 6 +++--- tests/unit/ReviewCommentTest.php | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/lib/Trolley/VerificationGateway.php b/lib/Trolley/VerificationGateway.php index c802c68..33f89df 100644 --- a/lib/Trolley/VerificationGateway.php +++ b/lib/Trolley/VerificationGateway.php @@ -18,7 +18,7 @@ public function __construct($gateway) public function search($query = []) { $response = $this->_http->get('/v1/verifications', $query); - return $this->buildCollection($response); + return $this->buildCollection($response, $query); } public function expire($body) @@ -44,13 +44,13 @@ public function trigger_watchlist($body) return $this->triggerWatchlist($body); } - private function buildCollection($response) + private function buildCollection($response, $query = []) { if ($response['ok']) { $pager = [ 'object' => $this, 'method' => 'search', - 'methodArgs' => [] + 'methodArgs' => $query ]; return new ResourceCollection($response, $response['verifications'], $pager); diff --git a/tests/unit/ReviewCommentTest.php b/tests/unit/ReviewCommentTest.php index db3830f..9a187a8 100644 --- a/tests/unit/ReviewCommentTest.php +++ b/tests/unit/ReviewCommentTest.php @@ -100,6 +100,32 @@ public function testBalanceSearchPaginationKeepsPathSegment() $this->assertSame(['page' => 2, 'pageSize' => 1], $http->requests[1][1]); } + public function testVerificationSearchPaginationKeepsQuery() + { + $http = new FakeHttp([ + [ + 'ok' => true, + 'verifications' => [['id' => 'V-1']], + 'meta' => ['page' => 1, 'pages' => 3, 'records' => 2], + ], + [ + 'ok' => true, + 'verifications' => [['id' => 'V-2']], + 'meta' => ['page' => 2, 'pages' => 3, 'records' => 2], + ], + ]); + $gateway = $this->gatewayWithHttp('Trolley\VerificationGateway', $http); + + $ids = []; + foreach ($gateway->search(['search' => 'Jane', 'pageSize' => 1]) as $verification) { + $ids[] = $verification['id']; + } + + $this->assertSame(['V-1', 'V-2'], $ids); + $this->assertSame('/v1/verifications', $http->requests[1][0]); + $this->assertSame(['page' => 2, 'search' => 'Jane', 'pageSize' => 1], $http->requests[1][1]); + } + private function attributesFor($model) { $property = new ReflectionProperty($model, '_attributes'); From 79f4020aefbdfaa6f8a3b14fe2fbd5b0345d27ce Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 04:16:06 +0000 Subject: [PATCH 6/7] Avoid paging verification mutations through search Co-authored-by: barnett --- lib/Trolley/ResourceCollection.php | 8 ++++++++ lib/Trolley/VerificationGateway.php | 4 ++-- tests/unit/ReviewCommentTest.php | 26 ++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/lib/Trolley/ResourceCollection.php b/lib/Trolley/ResourceCollection.php index b92ef58..85b2a98 100644 --- a/lib/Trolley/ResourceCollection.php +++ b/lib/Trolley/ResourceCollection.php @@ -106,6 +106,9 @@ public function valid() if ($this->_page + 1 >= $this->_maxPages) { return false; } + if (!$this->_hasPager()) { + return false; + } $this->_getNextPage(); } return $this->_index < $this->_records; @@ -124,6 +127,11 @@ private function _getNextPage() ++$this->_page; } + private function _hasPager() + { + return isset($this->_pager['object'], $this->_pager['method'], $this->_pager['methodArgs']); + } + /** * requests the next page of results for the collection * diff --git a/lib/Trolley/VerificationGateway.php b/lib/Trolley/VerificationGateway.php index 33f89df..6adc166 100644 --- a/lib/Trolley/VerificationGateway.php +++ b/lib/Trolley/VerificationGateway.php @@ -44,10 +44,10 @@ public function trigger_watchlist($body) return $this->triggerWatchlist($body); } - private function buildCollection($response, $query = []) + private function buildCollection($response, $query = null) { if ($response['ok']) { - $pager = [ + $pager = $query === null ? [] : [ 'object' => $this, 'method' => 'search', 'methodArgs' => $query diff --git a/tests/unit/ReviewCommentTest.php b/tests/unit/ReviewCommentTest.php index 9a187a8..b9cbe4b 100644 --- a/tests/unit/ReviewCommentTest.php +++ b/tests/unit/ReviewCommentTest.php @@ -126,6 +126,26 @@ public function testVerificationSearchPaginationKeepsQuery() $this->assertSame(['page' => 2, 'search' => 'Jane', 'pageSize' => 1], $http->requests[1][1]); } + public function testVerificationMutationCollectionDoesNotPageThroughSearch() + { + $http = new FakeHttp([ + [ + 'ok' => true, + 'verifications' => [['id' => 'V-1']], + 'meta' => ['page' => 1, 'pages' => 3, 'records' => 2], + ], + ]); + $gateway = $this->gatewayWithHttp('Trolley\VerificationGateway', $http); + + $ids = []; + foreach ($gateway->triggerWatchlist(['recipientId' => 'R-1']) as $verification) { + $ids[] = $verification['id']; + } + + $this->assertSame(['V-1'], $ids); + $this->assertSame([['post', '/v1/verifications/watchlist/trigger', ['recipientId' => 'R-1']]], $http->requests); + } + private function attributesFor($model) { $property = new ReflectionProperty($model, '_attributes'); @@ -159,4 +179,10 @@ public function get($path, $query = null) $this->requests[] = [$path, $query]; return array_shift($this->responses); } + + public function post($path, $body = null) + { + $this->requests[] = ['post', $path, $body]; + return array_shift($this->responses); + } } From 1fa03a577a7d24db512d4d4b5207c9484e1c51ae Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 27 May 2026 04:49:14 +0000 Subject: [PATCH 7/7] Prioritize internal pager arguments Co-authored-by: barnett --- lib/Trolley/BalanceGateway.php | 2 +- lib/Trolley/BatchGateway.php | 2 +- lib/Trolley/RecipientGateway.php | 2 +- tests/unit/ReviewCommentTest.php | 30 ++++++++++++++++++++++++++++-- 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/lib/Trolley/BalanceGateway.php b/lib/Trolley/BalanceGateway.php index 488a47b..6f10a53 100644 --- a/lib/Trolley/BalanceGateway.php +++ b/lib/Trolley/BalanceGateway.php @@ -50,7 +50,7 @@ public function search($params, $query) $pager = [ 'object' => $this, 'method' => 'searchPage', - 'methodArgs' => array_merge(['params' => $params], $query) + 'methodArgs' => array_merge($query, ['params' => $params]) ]; $items = array_map(function ($item) { diff --git a/lib/Trolley/BatchGateway.php b/lib/Trolley/BatchGateway.php index 1934d4b..797e4d4 100644 --- a/lib/Trolley/BatchGateway.php +++ b/lib/Trolley/BatchGateway.php @@ -216,7 +216,7 @@ private function paymentsCollection($response, $batchId, $params) { $pager = [ 'object' => $this, 'method' => 'paymentsInternal', - 'methodArgs' => array_merge(['batchId' => $batchId], $params), + 'methodArgs' => array_merge($params, ['batchId' => $batchId]), ]; $items = array_map(function ($item) { diff --git a/lib/Trolley/RecipientGateway.php b/lib/Trolley/RecipientGateway.php index 02a6b83..1a02b26 100644 --- a/lib/Trolley/RecipientGateway.php +++ b/lib/Trolley/RecipientGateway.php @@ -184,7 +184,7 @@ public function getAllOfflinePayments($recipientId, $query = []) return new ResourceCollection($response, $items, [ 'object' => $this, 'method' => 'getAllOfflinePaymentsPage', - 'methodArgs' => array_merge(['recipientId' => $recipientId], $query) + 'methodArgs' => array_merge($query, ['recipientId' => $recipientId]) ]); } else if ($response['errors']){ throw new Exception\Standard($response['errors']); diff --git a/tests/unit/ReviewCommentTest.php b/tests/unit/ReviewCommentTest.php index b9cbe4b..267cac6 100644 --- a/tests/unit/ReviewCommentTest.php +++ b/tests/unit/ReviewCommentTest.php @@ -39,7 +39,7 @@ public function testRecipientOfflinePaymentsPaginationKeepsRecipientId() $gateway = $this->gatewayWithHttp('Trolley\RecipientGateway', $http); $ids = []; - foreach ($gateway->getAllOfflinePayments('R-1', ['pageSize' => 1]) as $payment) { + foreach ($gateway->getAllOfflinePayments('R-1', ['recipientId' => 'R-wrong', 'pageSize' => 1]) as $payment) { $ids[] = $payment->id; } @@ -91,7 +91,7 @@ public function testBalanceSearchPaginationKeepsPathSegment() $gateway = $this->gatewayWithHttp('Trolley\BalanceGateway', $http); $accountNumbers = []; - foreach ($gateway->search('paypal', ['pageSize' => 1]) as $balance) { + foreach ($gateway->search('paypal', ['params' => 'wrong', 'pageSize' => 1]) as $balance) { $accountNumbers[] = $balance->accountNumber; } @@ -100,6 +100,32 @@ public function testBalanceSearchPaginationKeepsPathSegment() $this->assertSame(['page' => 2, 'pageSize' => 1], $http->requests[1][1]); } + public function testBatchPaymentsPaginationKeepsBatchId() + { + $http = new FakeHttp([ + [ + 'ok' => true, + 'payments' => [['id' => 'P-1']], + 'meta' => ['page' => 1, 'pages' => 3, 'records' => 2], + ], + [ + 'ok' => true, + 'payments' => [['id' => 'P-2']], + 'meta' => ['page' => 2, 'pages' => 3, 'records' => 2], + ], + ]); + $gateway = $this->gatewayWithHttp('Trolley\BatchGateway', $http); + + $ids = []; + foreach ($gateway->payments('B-1', ['batchId' => 'B-wrong', 'pageSize' => 1]) as $payment) { + $ids[] = $payment->id; + } + + $this->assertSame(['P-1', 'P-2'], $ids); + $this->assertSame('/v1/batches/B-1/payments', $http->requests[1][0]); + $this->assertSame(['page' => 2, 'batchId' => 'B-1', 'pageSize' => 1], $http->requests[1][1]); + } + public function testVerificationSearchPaginationKeepsQuery() { $http = new FakeHttp([