canonicalize static method
Builds the newline-delimited canonical string (cpt-user-attrs-v1) without a trailing newline.
Specification:
cpt-user-attrs-v1
<appKey>
<userToken>
<isPaying: true|false|unset>
<plan: value|null|unset>
<mrr: canonicalNumber|null|unset>
<currency: valueAsSent|unset>
<timestamp: epochSeconds>
Implementation
static String canonicalize({
required String appKey,
required String userToken,
required int timestamp,
Map<String, dynamic>? raw,
bool? isPaying,
String? plan,
num? mrr,
String? currency,
}) {
final effectiveRaw = <String, dynamic>{};
if (raw != null) {
effectiveRaw.addAll(raw);
} else {
if (isPaying != null) effectiveRaw['isPaying'] = isPaying;
if (plan != null) effectiveRaw['plan'] = plan;
if (mrr != null) effectiveRaw['mrr'] = mrr;
if (currency != null) effectiveRaw['currency'] = currency;
}
return [
version,
appKey,
userToken,
_canonicalField(
effectiveRaw,
'isPaying',
(v) => (v == true || v == 'true') ? 'true' : 'false',
),
_canonicalField(effectiveRaw, 'plan', (v) => v.toString()),
_canonicalField(effectiveRaw, 'mrr', (v) {
if (v is num) return canonicalNumber(v);
final parsed = num.tryParse(v.toString());
if (parsed != null) return canonicalNumber(parsed);
throw ArgumentError.value(v, 'mrr', 'mrr must be a number');
}),
_canonicalField(effectiveRaw, 'currency', (v) => v.toString()),
timestamp.toString(),
].join('\n');
}