w7corp/easywechat · error · RuntimeException
Invalid request resource.
Error message
Invalid request resource.
What it means
Thrown by Pay/Server::decodeJsonMessage() when resource.nonce or resource.associated_data is not a string. These two fields are mandatory inputs for AES-256-GCM decryption of resource.ciphertext; if either is missing or has the wrong type, decryption cannot proceed.
Source
Thrown at src/Pay/Server.php:274
protected function decodeJsonMessage(string $contents): array
{
$attributes = json_decode($contents, true);
if (! (is_array($attributes) && is_array($attributes['resource']))) {
throw new RuntimeException('Invalid request body.');
}
$resource = $attributes['resource'];
$ciphertext = $resource['ciphertext'] ?? null;
$nonce = $resource['nonce'] ?? null;
$associatedData = $resource['associated_data'] ?? null;
if (! is_string($ciphertext) || $ciphertext === '') {
throw new RuntimeException('Invalid request.');
}
if (! is_string($nonce) || ! is_string($associatedData)) {
throw new RuntimeException('Invalid request resource.');
}
$attributes = json_decode(
AesGcm::decrypt(
$ciphertext,
$this->merchant->getSecretKey(),
$nonce,
$associatedData,
),
true
);
if (! is_array($attributes)) {
throw new RuntimeException('Failed to decrypt request message.');
}
return $attributes;
}View on GitHub (pinned to f0cf0a8b83)
Solutions
- Log the raw body at the route and diff with a real WeChat notification (all three resource fields present, strings, possibly empty associated_data)
- Replay a genuine notification (WeChat Pay console -> re-push) instead of synthetic payloads
- If you build notifications yourself (sandbox), always include nonce and associated_data as strings
Example fix
// before - sandbox push missing nonce $resource = ['ciphertext' => $ct /* , 'nonce' forgotten */]; // after $resource = ['ciphertext' => $ct, 'nonce' => $nonce, 'associated_data' => 'transaction'];
Defensive patterns
Strategy: type-guard
Validate before calling
$ok = is_string($resource['nonce'] ?? null)
&& is_string($resource['associated_data'] ?? null);
if (! $ok) { return response('fail', 400); } Type guard
function hasGcmInputs(array $resource): bool
{
return is_string($resource['nonce'] ?? null) && is_string($resource['associated_data'] ?? null);
} Prevention
- Keep nonce and associated_data as strings when building sandbox notifications (empty string is fine, absence is not)
- Never array_filter notification resource objects
- Validate shape at the boundary and 400 early
When it happens
Trigger: A V3 notification whose resource object misses nonce or associated_data (or contains null/int), typically from hand-crafted test payloads, partial body truncation, or an intermediary re-encoding JSON and coercing values (e.g. empty nonce removed by an array_filter on the sender side).
Common situations: Dev fixtures built from documentation snippets that omit associated_data for older notifications; body clipping at size limits losing trailing fields; JSON re-serialization that converts '' to null.
Related errors
- Invalid request.
- Invalid event type.
- Invalid request body.
- V2 secret key is required.
- Failed to decrypt request message.
AI-assisted analysis of w7corp/easywechat@f0cf0a8b83 (2026-08-21).
Data as JSON: /api/errors/99df22283393f693.
Report an issue: GitHub.