Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Openapi Util Laravel Package

alibabacloud/openapi-util

Utility library for Alibaba Cloud OpenAPI PHP SDKs. Provides helpers for request signing, parameter encoding, and response parsing, so PHP applications can integrate smoothly with Alibaba Cloud services. A lightweight support package for building API clients.

View on GitHub
Deep Wiki
Context7

Getting Started

alibabacloud/openapi-util is not an SDK — it is the signing and encoding kernel that every generated alibabacloud/* SDK sits on. It contains one large static class, AlibabaCloud\OpenApiUtil\OpenApiUtilClient, which knows how to build canonicalized query strings, percent-encode parameters the Alibaba way, produce RPC/ROA/ACS3 signatures, generate Timestamp/Nonce values, and defensively read response bodies. For a Laravel developer it is the thing you reach for when (a) there is no generated SDK for the API you need, (b) you are debugging SignatureDoesNotMatch, or (c) you are writing a thin client around a handful of Alibaba Cloud endpoints and do not want to pull in Darabonba's entire gateway stack.

You almost certainly already have it. Check before installing:

composer show alibabacloud/openapi-util
# If absent (i.e. you have no Alibaba SDK at all):
composer require alibabacloud/openapi-util

Do not hand-pin the version if any generated SDK is present — those SDKs constrain it (~0.1, ~0.2, …) and Composer will resolve a compatible one for you.

Where to look first. Open vendor/alibabacloud/openapi-util/src/OpenApiUtilClient.php and skim the public static method list. It reads like a standard library: getTimestamp(), getNonce(), getRPCSignature(), getROASignature(), getSignature() (ACS3-HMAC-SHA256), getCanonicalizedQuery(), query(), percentEncode(), parseToMap(), readAsJSON(), assertAsMap(), is4xx(), is5xx(). Method names have shifted slightly across 0.x releases, so grep 'public static function' rather than trusting documentation.

First use case. Sign an RPC-style request by hand and fire it from php artisan tinker:

use AlibabaCloud\OpenApiUtil\OpenApiUtilClient;

$params = [
    'Format'           => 'JSON',
    'Version'          => '2017-05-25',
    'AccessKeyId'      => config('services.alibaba.key'),
    'SignatureMethod'  => 'HMAC-SHA1',
    'SignatureVersion' => '1.0',
    'SignatureNonce'   => OpenApiUtilClient::getNonce(),
    'Timestamp'        => OpenApiUtilClient::getTimestamp(),
    'PhoneNumbers'     => '13800000000',
    'TemplateCode'     => 'SMS_000000',
];

$params['Signature'] = OpenApiUtilClient::getRPCSignature(
    $params,
    'POST',
    config('services.alibaba.secret')
);

$response = Http::withBody(OpenApiUtilClient::query($params), 'application/x-www-form-urlencoded')
    ->post('https://dysmsapi.aliyuncs.com/');

If that returns {"Code":"OK"}, you have everything you need for the other 90% of hand-rolled Alibaba calls.

Implementation Patterns

Bind a signer as a singleton, not a facade call. The static class is impossible to mock and depends on ambient config; wrap it.

// app/Services/Alibaba/Signer.php
namespace App\Services\Alibaba;

use AlibabaCloud\OpenApiUtil\OpenApiUtilClient;

class Signer
{
    public function __construct(
        private string $accessKeyId,
        private string $accessKeySecret,
        private string $version,
    ) {}

    public function rpc(array $params, string $method = 'POST', ?string $nonce = null, ?string $timestamp = null): array
    {
        $params = array_merge([
            'Format'           => 'JSON',
            'Version'          => $this->version,
            'AccessKeyId'      => $this->accessKeyId,
            'SignatureMethod'  => 'HMAC-SHA1',
            'SignatureVersion' => '1.0',
            'SignatureNonce'   => $nonce ?? OpenApiUtilClient::getNonce(),
            'Timestamp'        => $timestamp ?? OpenApiUtilClient::getTimestamp(),
        ], $params);

        $params['Signature'] = OpenApiUtilClient::getRPCSignature(
            $params, $method, $this->accessKeySecret
        );

        return $params;
    }
}

The optional $nonce/$timestamp arguments are the extension point that makes the signer testable (see below). Register it in a service provider with credentials from config/services.php, sourced from env().

Send the body, do not let Guzzle re-encode it. Http::asForm() uses http_build_query with PHP_QUERY_RFC1738, which renders spaces as + and ~ as %7E. The signature was computed over RFC3986 output (%20, ~), so the server recomputes a different canonical string and rejects you with SignatureDoesNotMatch. Always pass the pre-encoded string:

$body = OpenApiUtilClient::query($params); // already sorted + percent-encoded

$response = Http::withHeaders(['Accept' => 'application/json'])
    ->withBody($body, 'application/x-www-form-urlencoded')
    ->timeout(10)
    ->retry(2, 200, throw: false)
    ->post($endpoint);

For ROA-style APIs (path + x-acs-* headers), build the string-to-sign yourself from the method, content hash, canonical headers, and path, then OpenApiUtilClient::getROASignature($stringToSign, $secret). This is the same primitive that alibabacloud/oss-util and OSS presigned URLs use.

Parse responses through the helpers, not raw json_decode. Alibaba Cloud returns inconsistent shapes on error — sometimes a string body, sometimes an XML envelope, sometimes null on 204.

$code = $response->status();
$json = OpenApiUtilClient::readAsJSON($response->toPsrResponse()->getBody());

if (OpenApiUtilClient::is5xx($code) || $json === null) {
    throw new AlibabaCloudException('Upstream unavailable', $code);
}

if (OpenApiUtilClient::is4xx($code)) {
    throw new AlibabaCloudException($json['Message'] ?? $json['Code'] ?? 'Unknown', $code);
}

$data = OpenApiUtilClient::assertAsMap($json['Data'] ?? []);

readAsJSON returns null rather than throwing on an empty or non-JSON body; assertAsMap/assertAsString/assertAsNumber throw on unset or wrong-typed values. Both behaviours are useful, but only if you wrap the assert family in your own exception so a malformed upstream response does not surface as a bare \Exception in your logs.

Queue jobs need a fresh signature per attempt, not per dispatch. SignatureNonce is single-use for a ~15-minute window. If you sign once in the job's constructor and let $tries = 5 replay it, attempts 2–5 all fail with SignatureNonceUsed even though the network error was transient. Sign inside handle().

public function handle(Signer $signer): void
{
    $params = $signer->rpc(['PhoneNumbers' => $this->phone, 'TemplateCode' => '…']);
    // …send
}

Testing. Two complementary approaches:

  1. Contract test — Http::fake() and assert the outbound body contains Signature=, a Timestamp matching /Z$/, and a SignatureNonce.
  2. Golden-signature test — inject a fixed nonce ("test-nonce") and timestamp ("2026-01-15T00:00:00Z") through the optional constructor arguments, then assertSame('knownBase64…', $signed['Signature']). This is the only way to catch a silent encoding regression introduced by a composer update of the util package, and it is why the two extra parameters on rpc() are worth having.

Mock App\Services\Alibaba\Signer (not the static class) everywhere else.

Gotchas and Tips

  • Clock skew is the #1 false alarm. getTimestamp() emits UTC Y-m-d\TH:i:s\Z. A container more than 15 minutes off yields InvalidTimeStamp.Expired, which looks exactly like a credential problem. Check date -u in the container before rotating keys.
  • Never sign an already-signed array. Signature must be absent from the input to getRPCSignature(). Merging a stale $params from config, or adding Signature before calling, silently corrupts the canonical string.
  • The HTTP method is part of the string-to-sign. Signing for POST and dispatching GET (or vice versa) fails. Pick the verb once and thread it through your wrapper as a single value.
  • Nested arrays and integer keys break canonicalization. Flatten parameters or JSON-encode them before signing; ksort() on an array containing a nested array produces a canonical string the server will never reproduce.
  • Application vs Application::class. This package is pure utility — no credentials provider, no retry policy, no endpoint resolver. Those live in alibabacloud/credentials, alibabacloud/tea, and alibabacloud/darabonba-openapi. Do not expect Laravel-style config, service providers, or facades; there are none.
  • "Call to undefined method getSignature()" means a downgraded util. When one generated SDK pins ~0.1 and another needs the ACS3 (getSignature()) API, Composer may hoist the older one and break the newer client. Fix with an explicit composer require alibabacloud/openapi-util:^0.2 and re-run the SDK's tests.
  • PHP 8.2+ deprecations. Older 0.1.x releases have implicitly nullable parameters that emit Deprecated: notices under Laravel's error handler; if your log fills up after an upgrade, bump the util package rather than suppressing E_DEPRECATED.
  • readAsSSE() needs a streamed response. If you consume a streaming endpoint, pipe it through response()->stream() (or a queued chunked read) — buffering the whole body into a string defeats the purpose and will time out on long-lived connections.
  • Log the canonical string, never the signature. When debugging, dump OpenApiUtilClient::query($paramsWithoutSignature) and compare it character-by-character against the Alibaba Cloud signature tool. Logging the full signed params leaks a valid credential into your log stack for the nonce lifetime.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager