diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index fa128798..c1156d29 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -113,14 +113,12 @@ jobs: cc <@france.berut> <@khadija.cherif> - name: Send changelog to Slack - uses: slackapi/slack-github-action@v1.27.0 + uses: slackapi/slack-github-action@v2.0.0 with: - channel-id: CR9C57YM6 - slack-message: ${{ steps.slack-markdown-release-notes.outputs.text }} + method: chat.postMessage + token: ${{ secrets.SLACK_RELEASE_CHANGELOG_BOT_TOKEN }} payload: | - { - "username": "${{ github.event.sender.login }}", - "icon_url": "${{ github.event.sender.avatar_url }}" - } - env: - SLACK_BOT_TOKEN: ${{ secrets.SLACK_RELEASE_CHANGELOG_BOT_TOKEN }} + channel: CR9C57YM6 + username: "${{ github.event.sender.login }}" + icon_url: "${{ github.event.sender.avatar_url }}" + text: ${{ toJson(steps.slack-markdown-release-notes.outputs.text) }} diff --git a/CHANGELOG.md b/CHANGELOG.md index ef8c877f..9ed0b1a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # CHANGELOG +## v2.4.0 - 2024-12-09 + +### Changes + +### 🚀 New Features + +- New endpoint add order status by payment route (#155) + +#### Contributors + +@Francois-Gomis, @alma-renovate-bot, @alma-renovate-bot[bot], @github-actions and @remi-zuffinetti + ## v2.3.1 - 2024-11-14 ### Changes @@ -192,6 +204,7 @@ + ``` * Add fields and docs to the Payment entity * Add a Refund entity and extract refunds data within the Payment entity constructor diff --git a/composer.json b/composer.json index 2be1d8ba..e60af78a 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "alma/alma-php-client", "description": "PHP API client for the Alma payments API", - "version": "2.3.1", + "version": "2.4.0", "type": "library", "require": { "php": "^5.6 || ~7.0 || ~7.1 || ~7.2 || ~7.3 || ~7.4 || ~8.0 || ~8.1 || ~8.2 || ~8.3", diff --git a/src/Client.php b/src/Client.php index 41d13d7f..10f71ec5 100644 --- a/src/Client.php +++ b/src/Client.php @@ -30,7 +30,7 @@ class Client { - const VERSION = '2.3.1'; + const VERSION = '2.4.0'; const LIVE_MODE = 'live'; const TEST_MODE = 'test'; diff --git a/src/Endpoints/Payments.php b/src/Endpoints/Payments.php index 0661f546..6f7b77c2 100644 --- a/src/Endpoints/Payments.php +++ b/src/Endpoints/Payments.php @@ -28,7 +28,9 @@ use Alma\API\Endpoints\Results\Eligibility; use Alma\API\Exceptions\ParametersException; use Alma\API\Exceptions\RequestException; +use Alma\API\Lib\BoolUtils; use Alma\API\Lib\PaymentValidator; +use Alma\API\Lib\StringUtils; use Alma\API\ParamsError; use Alma\API\Payloads\Refund; use Alma\API\Entities\Order; @@ -38,15 +40,15 @@ class Payments extends Base { - const PAYMENTS_PATH = '/v1/payments'; - const ELIGIBILITY_PATH = '/v1/payments/eligibility'; + const PAYMENTS_PATH = '/v1/payments'; + const ELIGIBILITY_PATH = '/v1/payments/eligibility'; const ELIGIBILITY_PATH_V2 = '/v2/payments/eligibility'; /** - * @param array $data Payment data to check the eligibility for – same data format as payment creation, + * @param array $data Payment data to check the eligibility for – same data format as payment creation, * except that only payment.purchase_amount is mandatory and payment.installments_count * can be an array of integers, to test for multiple eligible plans at once. - * @param bool $raiseOnError Whether to raise a RequestError on 4xx and 5xx errors, as it should. + * @param bool $raiseOnError Whether to raise a RequestError on 4xx and 5xx errors, as it should. * Defaults false to preserve original behaviour. Will default to true in future * versions (next major update). * @@ -126,7 +128,7 @@ public function create($data) } /** - * @param string $id The ID of the payment to cancel + * @param string $id The ID of the payment to cancel * * @return void * @throws RequestError @@ -190,13 +192,13 @@ public function edit($id, $data) } /** - * @param string $id The ID of the payment to flag as potential fraud - * @param string $reason An optional message indicating why this payment is being flagged + * @param string $id The ID of the payment to flag as potential fraud + * @param string $reason An optional message indicating why this payment is being flagged * * @return bool * @throws RequestError */ - public function flagAsPotentialFraud($id, $reason=null) + public function flagAsPotentialFraud($id, $reason = null) { $req = $this->request(self::PAYMENTS_PATH . "/$id/potential-fraud"); @@ -225,7 +227,8 @@ public function flagAsPotentialFraud($id, $reason=null) * @throws RequestError * @throws RequestException */ - public function partialRefund($id, $amount, $merchantReference = "", $comment = "") { + public function partialRefund($id, $amount, $merchantReference = "", $comment = "") + { return $this->doRefund( Refund::create($id, $amount, $merchantReference, $comment) ); @@ -242,7 +245,8 @@ public function partialRefund($id, $amount, $merchantReference = "", $comment = * @throws RequestError * @throws RequestException */ - public function fullRefund($id, $merchantReference = "", $comment = "") { + public function fullRefund($id, $merchantReference = "", $comment = "") + { return $this->doRefund( Refund::create($id, null, $merchantReference, $comment) ); @@ -257,7 +261,8 @@ public function fullRefund($id, $merchantReference = "", $comment = "") { * @throws RequestException * @throws ParametersException */ - private function doRefund(Refund $refundPayload) { + private function doRefund(Refund $refundPayload) + { $id = $refundPayload->getId(); $req = $this->request(self::PAYMENTS_PATH . "/$id/refund"); @@ -285,7 +290,8 @@ private function doRefund(Refund $refundPayload) { * @throws RequestException * @deprecated please use `partialRefund` or `fullRefund` */ - public function refund($id, $totalRefund = true, $amount = null, $merchantReference = "") { + public function refund($id, $totalRefund = true, $amount = null, $merchantReference = "") + { if ($totalRefund !== true) { return $this->partialRefund($id, $amount, $merchantReference); } @@ -335,6 +341,38 @@ public function addOrder($id, $orderData, $overwrite = false) return new Order(end($res->json)); } + /** + * Add order status to Alma Order by merchant_order_reference + * + * @param string $paymentId + * @param string $merchantOrderReference + * @param string $status + * @param bool | null $isShipped + * @return void + * @throws ParametersException + * @throws RequestError + * @throws RequestException + */ + public function addOrderStatusByMerchantOrderReference( + $paymentId, + $merchantOrderReference, + $status, + $isShipped = null + ) + { + $this->checkAddOrderStatusParams($paymentId, $merchantOrderReference, $status, $isShipped); + + $orderStatus = ['status' => $status, 'is_shipped' => $isShipped]; + $res = $this->request(self::PAYMENTS_PATH . "/$paymentId/orders/$merchantOrderReference/status") + ->setRequestBody($orderStatus) + ->post(); + + if ($res->isError()) { + throw new RequestException($res->errorMessage, null, $res); + } + + } + /** * Sends a SMS to the customer, containing a link to the payment's page * /!\ Your account must be authorized by Alma to use that endpoint; it will otherwise fail with a 403 error @@ -355,4 +393,30 @@ public function sendSms($id) return true; } + /** + * Check add order status params type + * + * @param $paymentId + * @param $merchantOrderReference + * @param $status + * @param $isShipped + * @return void + * @throws ParametersException + */ + private function checkAddOrderStatusParams($paymentId, $merchantOrderReference, $status, $isShipped) + { + if (!StringUtils::isAValidString($paymentId)) { + throw new ParametersException("Payment id must be a string"); + } + if (!StringUtils::isAValidString($merchantOrderReference)) { + throw new ParametersException("Order merchant reference must be a string"); + } + if (!StringUtils::isAValidString($status)) { + throw new ParametersException("Order merchant reference must be a string"); + } + if (!BoolUtils::isStrictlyBoolOrNull($isShipped)) { + throw new ParametersException("Is shipped must be null or boolean"); + } + } + } diff --git a/src/Lib/BoolUtils.php b/src/Lib/BoolUtils.php new file mode 100644 index 00000000..6ec0898b --- /dev/null +++ b/src/Lib/BoolUtils.php @@ -0,0 +1,22 @@ +responseMock->shouldReceive('isError')->once()->andReturn(true); + $this->responseMock->errorMessage = 'Error in response'; $this->requestObject->shouldReceive('setRequestBody') ->with(['collect_data_url' => self::URL]) ->andReturn($this->requestObject); diff --git a/tests/Unit/Endpoints/InsuranceTest.php b/tests/Unit/Endpoints/InsuranceTest.php index d0c8a6a1..7fe4cad7 100644 --- a/tests/Unit/Endpoints/InsuranceTest.php +++ b/tests/Unit/Endpoints/InsuranceTest.php @@ -50,6 +50,12 @@ class InsuranceTest extends TestCase */ private $insuranceMock; + /** @var InsuranceValidator */ + private $insuranceValidatorMock; + + /** @var ArrayUtils */ + private $arrayUtilsMock; + /** * @return void diff --git a/tests/Unit/Endpoints/PaymentsTest.php b/tests/Unit/Endpoints/PaymentsTest.php index d6027b1f..7dd4c8f1 100644 --- a/tests/Unit/Endpoints/PaymentsTest.php +++ b/tests/Unit/Endpoints/PaymentsTest.php @@ -4,6 +4,7 @@ use Alma\API\Exceptions\ParametersException; use Alma\API\Exceptions\RequestException; +use Alma\API\RequestError; use Alma\API\Response; use Mockery; use PHPUnit\Framework\TestCase; @@ -451,4 +452,153 @@ public function testFullRefundRequestError() $payments->fullRefund($id); } + /** + * @dataProvider addOrderStatusErrorPayloadProvider + * @param $paymentId + * @param $merchantOrderReference + * @param $status + * @param null $isShipped + * @return void + * @throws ParametersException + * @throws RequestException + * @throws RequestError + */ + public function testAddOrderStatusThrowParametersException( + $paymentId, + $merchantOrderReference, + $status, + $isShipped = null + ) + { + $paymentEndpoint = Mockery::mock(Payments::class)->makePartial(); + $this->expectException(ParametersException::class); + $paymentEndpoint->addOrderStatusByMerchantOrderReference( + $paymentId, + $merchantOrderReference, + $status, + $isShipped + ); + } + + public function testAddIOrderStatusThrowRequestExceptionForNon200Return() + { + $clientContext = Mockery::mock(ClientContext::class); + $paymentEndpoint = Mockery::mock(Payments::class)->makePartial(); + $requestObjectMock = Mockery::mock(Request::class); + $responseMock = Mockery::mock(Response::class); + $responseMock->errorMessage = 'Error in request'; + $responseMock->shouldReceive('isError')->andReturn(true); + $requestObjectMock->shouldReceive('setRequestBody') + ->once() + ->with(['status' => 'in progress', 'is_shipped' => false]) + ->andReturn($requestObjectMock); + $requestObjectMock->shouldReceive('post') + ->once() + ->andReturn($responseMock); + $paymentEndpoint->shouldReceive('request') + ->with("/v1/payments/payment_42/orders/ref_3546/status") + ->once() + ->andReturn($requestObjectMock); + $paymentEndpoint->setClientContext($clientContext); + + $this->expectException(RequestException::class); + + $paymentEndpoint->addOrderStatusByMerchantOrderReference( + 'payment_42', + 'ref_3546', + 'in progress', + false + ); + } + + /** + * + * @dataProvider AddOrderStatusProvider + * @param $paymentId + * @param $merchantOrderReference + * @param $status + * @param null $isShipped + * @return void + * @throws ParametersException + * @throws RequestError + * @throws RequestException + */ + public function testAddOrderStatusReturnVoidFor204return( + $paymentId, + $merchantOrderReference, + $status, + $isShipped = null + ) + { + $clientContext = Mockery::mock(ClientContext::class); + $paymentEndpoint = Mockery::mock(Payments::class)->makePartial(); + $requestObjectMock = Mockery::mock(Request::class); + $responseMock = Mockery::mock(Response::class); + $responseMock->shouldReceive('isError')->andReturn(false); + $requestObjectMock->shouldReceive('setRequestBody') + ->once() + ->with(['status' => $status, 'is_shipped' => $isShipped]) + ->andReturn($requestObjectMock); + $requestObjectMock->shouldReceive('post') + ->once() + ->andReturn($responseMock); + $paymentEndpoint->shouldReceive('request') + ->with("/v1/payments/$paymentId/orders/$merchantOrderReference/status") + ->once() + ->andReturn($requestObjectMock); + $paymentEndpoint->setClientContext($clientContext); + + $paymentEndpoint->addOrderStatusByMerchantOrderReference( + $paymentId, + $merchantOrderReference, + $status, + $isShipped + ); + } + + public static function AddOrderStatusProvider() + { + return [ + 'With is shipped null' => [ + 'paymentId' => 'payment_1234', + 'merchantOrderReference' => 'merchant_order_123', + 'status' => 'status_shipped' + ], + 'With is shipped bool' => [ + 'paymentId' => 'payment_1234', + 'merchantOrderReference' => 'merchant_order_123', + 'status' => 'status_shipped', + 'isShipped' => true + ] + ]; + } + + public static function addOrderStatusErrorPayloadProvider() + { + return [ + 'Payment Id not a string' => [ + 'paymentId' => 1232214, + 'merchantOrderReference' => 'merchant_order2', + 'status' => 'shipped' + ], + 'Merchant order reference is not a string' => [ + 'paymentId' => 'payment_124', + 'merchantOrderReference' => 421, + 'status' => 'shipped' + ], + 'status is not a string' => [ + 'paymentId' => 'payment_124', + 'merchantOrderReference' => 'merchant_order1', + 'status' => true + ], + 'is Shipped is not a bool' => [ + 'paymentId' => 'payment_124', + 'merchantOrderReference' => 'merchant_order1', + 'status' => 'Shipped', + 'isShipped' => 'test' + ] + + ]; + } + } diff --git a/tests/Unit/Entities/Insurance/FileTest.php b/tests/Unit/Entities/Insurance/FileTest.php index 965f78fb..813656d0 100644 --- a/tests/Unit/Entities/Insurance/FileTest.php +++ b/tests/Unit/Entities/Insurance/FileTest.php @@ -11,6 +11,10 @@ class FileTest extends TestCase * @var File $file */ protected $file; + /** + * @var string[] + */ + protected $fileData; /** * @return void diff --git a/tests/Unit/Lib/ArrayUtilsTest.php b/tests/Unit/Lib/ArrayUtilsTest.php index 7d00132a..5d7bcf06 100644 --- a/tests/Unit/Lib/ArrayUtilsTest.php +++ b/tests/Unit/Lib/ArrayUtilsTest.php @@ -9,6 +9,10 @@ class ArrayUtilsTest extends TestCase { + /** + * @var ArrayUtils + */ + private $arrayUtils; public function setUp(): void { $this->arrayUtils = new ArrayUtils();