$productId, 'unit_amount' => $amountCents, 'currency' => strtolower($currency), 'interval' => $interval, 'metadata' => $metadata, ]; } /** What counts as "the same call" for a Product — same reason as priceParameters(). */ public static function productParameters(string $name, array $metadata): array { return ['name' => $name, 'metadata' => $metadata]; } /** * Eight hex characters over the parameters, canonically ordered. * * Ordered, because PHP keeps insertion order and two callers writing the * same metadata in a different order would otherwise send two keys for one * call — which would mint two Prices and be a worse bug than the one this * closes. */ public static function fingerprint(array $parameters): string { return substr(sha1(self::canonical($parameters)), 0, 8); } private static function with(?string $key, array $parameters): ?string { // Null stays null: a caller who sends no key wants no replay, and // inventing one here would change what the call means. return $key === null ? null : $key.'-'.self::fingerprint($parameters); } private static function canonical(array $parameters): string { ksort($parameters); foreach ($parameters as $name => $value) { $parameters[$name] = is_array($value) ? self::canonical($value) : (string) $value; } return json_encode($parameters, JSON_THROW_ON_ERROR); } }