Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/direct/commerce-contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export const DIRECT_PRICING_FAQ_ITEMS = [
{
question: "What is your refund policy?",
answer:
"Direct refund requests are reviewed case by case. A refund and cancellation of automatic renewal are separate actions, so tell support which outcome you need.",
"You can cancel and get a full refund within 14 days of any Direct payment, without giving a reason. Later requests are reviewed case by case. A refund and cancellation of automatic renewal are separate actions, so tell support which outcome you need.",
},
{
question: "What happens after my subscription expires?",
Expand Down
4 changes: 2 additions & 2 deletions lib/direct/support-content.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
export const DIRECT_SUPPORT_REFUND_TITLE = "Direct purchase refunds";
export const DIRECT_SUPPORT_REFUND_BODY =
"Direct refund requests are reviewed case by case. A refund and cancellation of automatic renewal are separate actions.";
"You can cancel and request a full refund within 14 days of any Direct payment, without giving a reason. Later requests are reviewed case by case. A refund and cancellation of automatic renewal are separate actions.";
export const DIRECT_SUPPORT_REFUND_FAQ =
"Choose “Paddle refund (direct purchase)” in the support form and use the email address from your Paddle receipt. Add the transaction ID and say whether you want a refund only or a refund plus cancellation of automatic renewal. Requests are reviewed case by case. Mac App Store purchases are refunded by Apple.";
"Choose “Paddle refund (direct purchase)” in the support form and use the email address from your Paddle receipt. Add the transaction ID and say whether you want a refund only or a refund plus cancellation of automatic renewal. Refunds requested within 14 days of a payment are granted without giving a reason; later requests are reviewed case by case. Mac App Store purchases are refunded by Apple.";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Allow eligible refunds without a reason

When a customer follows this FAQ's instruction to use the support form, they still cannot request the promised refund without supplying a reason: SupportForm labels the refund details field “Why would you like a refund?”, marks it required with a 20-character minimum (components/organisms/SupportForm.tsx:321-331), and the server schema independently enforces that minimum (lib/support-request.ts:46-52). This makes the advertised no-reason policy impossible to use through the documented refund workflow, so the refund path should make that field optional and avoid asking why for eligible requests.

Useful? React with 👍 / 👎.


export const HORA_INSTALLATION_FAQ =
"You can download the Direct edition from horacal.app and choose a plan in the app, install hora from the Mac App Store, or use it through Setapp. Direct includes a 7-day cardless trial before you choose Monthly or Annual.";
8 changes: 6 additions & 2 deletions scripts/verify-sanity-site.ts
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,8 @@ function validatePricing(document: SiteDocument) {
expectCopy(publicCopy, "7-day", "pricingPage public copy");
expectCopy(publicCopy, "cardless", "pricingPage public copy");
expectCopy(publicCopy, "no new lifetime", "pricingPage public copy");
for (const forbidden of ["14-day", "14 day", "24-hour", "24 hour"]) {
expectCopy(publicCopy, "14 days", "pricingPage public copy");
for (const forbidden of ["24-hour", "24 hour"]) {
expect(!publicCopy.includes(forbidden), `pricingPage public copy must not promise ${forbidden}`);
}
}
Expand Down Expand Up @@ -604,13 +605,15 @@ function validateLegal(document: SiteDocument, kind: "privacy" | "terms" | "refu

if (kind === "refunds") {
const publicCopy = searchableText(document.body);
expectCopy(publicCopy, "within 14 days", "refundsPage.body");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Align the manual Sanity manifest with the new policy

A release operator following the repository's manual Sanity instructions cannot satisfy this new requirement: docs/DirectSanityContentManifest.md:20-48 still directs them to remove every fixed 14-day promise and publish case-by-case-only pricing and refunds text. Applying that manifest now either removes the guarantee from production or makes sanity:verify:site fail, so the manifest needs to be updated alongside these guardrails.

Useful? React with 👍 / 👎.

expectCopy(publicCopy, "without giving a reason", "refundsPage.body");
expectCopy(publicCopy, "reviewed case by case", "refundsPage.body");
expectCopy(
publicCopy,
"refund and cancellation of automatic renewal are separate actions",
"refundsPage.body",
);
for (const forbidden of ["14-day", "14 day", "24-hour", "24 hour"]) {
for (const forbidden of ["24-hour", "24 hour"]) {
expect(!publicCopy.includes(forbidden), `refundsPage.body must not promise ${forbidden}`);
}
}
Expand All @@ -635,6 +638,7 @@ function validateCodeOwnedDirectSupport() {
DIRECT_SUPPORT_REFUND_FAQ,
]);
expectCopy(publicCopy, "direct purchase refunds", "support Direct refund copy");
expectCopy(publicCopy, "within 14 days", "support Direct refund copy");
expectCopy(publicCopy, "reviewed case by case", "support Direct refund copy");
expectCopy(
publicCopy,
Expand Down
4 changes: 3 additions & 1 deletion tests/direct-commerce-contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ test("keeps Direct new-sale pricing in code while Sanity controls download visib
const publicCopy = JSON.stringify(DIRECT_PRICING_FAQ_ITEMS).toLowerCase();
assert.match(publicCopy, /7-day cardless trial/);
assert.match(publicCopy, /no new lifetime plan/);
assert.doesNotMatch(publicCopy, /14-day|14 day|24-hour|24 hour/);
assert.match(publicCopy, /within 14 days of any direct payment/);
assert.match(publicCopy, /reviewed case by case/);
assert.doesNotMatch(publicCopy, /24-hour|24 hour/);
assert.equal(
DIRECT_CHECKOUT_PRICE_NOTE,
"Choose a plan in the app. Final currency and applicable taxes are confirmed in checkout.",
Expand Down