fix(discord): show old → new plan on plan change notification (#637)
## Problem The Discord **Plan changed** notification only showed the *new* plan value in the `Changed` field, so it was impossible to tell what actually changed: ``` Changed Plan: €3.99 / month ``` Was it a price change? An interval change? From which plan? No way to know. ## Fix Compute the previous plan from Stripe's `previous_attributes` and render it as `old → new`, matching the existing status-change behaviour: ``` Changed Plan: €9.99 / month → €3.99 / month ``` `planLabel()` was generalised to read both the modern `items.data[].price` shape and the legacy top-level `plan` shape Stripe still includes in the diff, so the same helper works on both the current object and the `previous_attributes` diff. Falls back to the old `Plan: X` output when no previous value is detectable. ## Test Added a feature test asserting the `old → new` output for a `customer.subscription.updated` plan change. All 11 tests in the file pass.
This commit is contained in:
parent
02087abcc7
commit
3972007844
|
|
@ -206,34 +206,48 @@ class PostStripeEventToDiscord implements ShouldQueue
|
|||
}
|
||||
|
||||
if (array_key_exists('items', $previous) || array_key_exists('plan', $previous)) {
|
||||
$lines[] = 'Plan: '.($this->planLabel($object) ?? 'updated');
|
||||
$new = $this->planLabel($object) ?? 'updated';
|
||||
$old = $this->planLabel($previous);
|
||||
$lines[] = ($old !== null && $old !== $new)
|
||||
? sprintf('Plan: %s → %s', $old, $new)
|
||||
: 'Plan: '.$new;
|
||||
}
|
||||
|
||||
return $lines === [] ? null : implode("\n", $lines);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $object
|
||||
* Build a "€3.99 / month" label from either a subscription object, or a
|
||||
* previous_attributes diff. Handles both the modern `items.data[].price`
|
||||
* shape and the legacy top-level `plan` shape Stripe still includes.
|
||||
*
|
||||
* @param array<string, mixed> $source
|
||||
*/
|
||||
private function planLabel(array $object): ?string
|
||||
private function planLabel(array $source): ?string
|
||||
{
|
||||
$price = $object['items']['data'][0]['price'] ?? null;
|
||||
$price = $source['items']['data'][0]['price'] ?? $source['plan'] ?? null;
|
||||
|
||||
if (! is_array($price) || ! isset($price['unit_amount'])) {
|
||||
if (! is_array($price)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$amount = $this->money((int) $price['unit_amount'], (string) ($price['currency'] ?? 'usd'));
|
||||
$recurring = $price['recurring'] ?? null;
|
||||
$amount = $price['unit_amount'] ?? $price['amount'] ?? null;
|
||||
|
||||
if (! is_array($recurring) || ! isset($recurring['interval'])) {
|
||||
return $amount;
|
||||
if ($amount === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$label = $this->money((int) $amount, (string) ($price['currency'] ?? 'usd'));
|
||||
$recurring = is_array($price['recurring'] ?? null) ? $price['recurring'] : $price;
|
||||
|
||||
if (! isset($recurring['interval'])) {
|
||||
return $label;
|
||||
}
|
||||
|
||||
$count = (int) ($recurring['interval_count'] ?? 1);
|
||||
$interval = $count > 1 ? sprintf('%d %ss', $count, $recurring['interval']) : (string) $recurring['interval'];
|
||||
|
||||
return sprintf('%s / %s', $amount, $interval);
|
||||
return sprintf('%s / %s', $label, $interval);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -159,6 +159,31 @@ test('reports the status change on a meaningful update', function () {
|
|||
->contains(fn ($field) => $field['name'] === 'Changed' && str_contains($field['value'], 'trialing → active')));
|
||||
});
|
||||
|
||||
test('reports the old and new plan on a plan change', function () {
|
||||
Http::fake();
|
||||
|
||||
$price = fn (int $amount) => [
|
||||
'items' => ['data' => [['price' => [
|
||||
'unit_amount' => $amount,
|
||||
'currency' => 'eur',
|
||||
'recurring' => ['interval' => 'month', 'interval_count' => 1],
|
||||
]]]],
|
||||
];
|
||||
|
||||
handleStripeWebhook([
|
||||
'id' => 'evt_plan_change',
|
||||
'type' => 'customer.subscription.updated',
|
||||
'data' => [
|
||||
'object' => ['id' => 'sub_1', 'status' => 'active', 'customer' => 'cus_1'] + $price(399),
|
||||
'previous_attributes' => $price(999),
|
||||
],
|
||||
]);
|
||||
|
||||
Http::assertSent(fn ($request) => str_contains($request['embeds'][0]['title'], 'Plan changed')
|
||||
&& collect($request['embeds'][0]['fields'])
|
||||
->contains(fn ($field) => $field['name'] === 'Changed' && str_contains($field['value'], '€9.99 / month → €3.99 / month')));
|
||||
});
|
||||
|
||||
test('ignores trivial subscription updates', function () {
|
||||
Http::fake();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue