Skip to content

Commit b8aa513

Browse files
maskedsyntaxclaude
andcommitted
chore: raise Patterns Pro to $39.99 across the site and store assets
The rise was scheduled for 15 August 2026 and did not go through. It was applied manually in App Store Connect on 16 August and confirmed live on a device reading the real storefront, so the copy now states $39.99 as the current price rather than announcing it as upcoming. The app needs no change for this. It renders whatever StoreKit returns, so existing installs pick the new price up on their next launch with no build, no update and no review. Only hand-written copy could go stale, which is what this commit fixes: - JSON-LD priceSpecification and the FAQ answer, both of which feed structured data that search engines repeat back as the current price - the roadmap card, moved out of "Almost ready" now that it has shipped - the pricing paragraph in the between-therapy-sessions post - the Pro value card in the App Store screenshot source The FAQ now says $39.99 in the US with local pricing elsewhere, because Apple generates the other storefronts from the US base and they are not all equivalents. copy-guards.test.ts is inverted to match: it required $14.99 and required $39.99 be described as upcoming, and now asserts the opposite and that the retired figure appears nowhere quotable. PUBLISHING_GUIDE.md records the switch-over procedure, the two traps that made this hard to verify (a local Patterns.storekit masks the live price, and flutter run does not apply the scheme's StoreKit config), and why Pro stays a one-time purchase. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 5a4e981 commit b8aa513

9 files changed

Lines changed: 102 additions & 29 deletions

File tree

PUBLISHING_GUIDE.md

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ Patterns has been rebuilt around a calmer first session and one clear next step.
5757
- Rebuilt Insights with clearer trends, consistency, and practice context
5858
- Faster journal navigation, search, and date-based entries
5959
- Optional app lock and improved privacy explanations
60-
- Patterns Pro is now available as a one-time unlockno subscription
60+
- Patterns Pro is now available as a one-time unlock, with no subscription
6161

6262
Your journal entries, OCD records, and product-usage counters remain on-device.
6363

@@ -66,9 +66,41 @@ Your journal entries, OCD records, and product-usage counters remain on-device.
6666
`ocd,erp,compulsion,intrusive thoughts,journal,anxiety,exposure,habit,cbt,self care`
6767

6868
### Pricing
69-
- **Patterns Pro:** US$14.99 one-time purchase through August/September 2026.
70-
- **Planned price:** US$19.99 one-time purchase after the introductory period.
71-
- Present US$14.99 as the current price, not as a guaranteed permanent or discounted price, unless a formal store promotion is configured.
69+
- **Patterns Pro (iOS, Android):** **US$39.99** one-time purchase, with local pricing elsewhere (Apple generates those from the US base price, so they are not all $39.99 equivalents). Raised from US$14.99 on 16 August 2026. The rise was first scheduled for 15 August 2026 and did not go through; it was applied manually the next day.
70+
- **Patterns Desktop Pro (macOS, Windows, Linux):** US$9.99 one-time licence via Lemon Squeezy. Unchanged.
71+
- Pro is a **one-time purchase and stays one**. There is no subscription, and no annual plan. Do not describe it as one anywhere.
72+
- Present the price that is actually being charged right now, which is US$39.99. Do not quote US$14.99 anywhere, and never announce a future price change with a date attached. A date the store has not honoured reads as though the rise already happened, and dangles a deadline discount nobody can take.
73+
- Existing Pro owners keep their access and are never charged again. Say so wherever the new price is mentioned.
74+
75+
#### How the rise to US$39.99 is made
76+
The product ID **`com.maskedsyntax.patterns.pro` never changes**. It is a
77+
non-consumable, so raising its price does not affect anyone who already owns it:
78+
neither store re-charges for a non-consumable, and restore keeps returning it.
79+
80+
- Apply the price change to the existing product in **App Store Connect** and **Google Play Console**.
81+
- **Never** delete, deactivate or rename the product. That is the one action that would strand every existing purchaser, because restore would stop returning it.
82+
- No new in-app product is created. There is nothing to migrate.
83+
- **Pro stays a one-time purchase.** No subscription tier, annual or otherwise. This was reconsidered and rejected on 16 August 2026: "never a subscription" is stated in the app paywall, four pages of the site and four blog posts, and `website/src/lib/data/copy-guards.test.ts` fails the build on any per-year price string. Adding one would cost more in trust than it returns.
84+
85+
#### Switching the copy over (done 16 August 2026)
86+
Completed for the rise to US$39.99. Kept as the procedure for any future change.
87+
Do not run it until the paywall shows the new price on a device with **no local
88+
StoreKit config**, which is the only check that reads the real storefront. A
89+
simulator using `ios/Patterns.storekit` shows that file's price regardless of
90+
the live storefront, so it proves nothing. `flutter run` does not apply the
91+
scheme's StoreKit config either, because it installs via simctl.
92+
93+
1. `website/src/routes/+page.svelte` - the JSON-LD `priceSpecification.price`. This is the figure search engines repeat, so it goes stale most visibly.
94+
2. `website/src/lib/data/faq.ts` - the cost answer. Also feeds FAQPage structured data.
95+
3. `website/src/routes/roadmap/+page.svelte` - move the pricing card between columns so it is not still sitting in "Almost ready" after it has shipped.
96+
4. `website/src/content/blog/between-therapy-sessions.md` - the pricing paragraph.
97+
5. This file: the two Pricing bullets above.
98+
6. `website/src/lib/data/copy-guards.test.ts` - invert the date-sensitive guards so they assert the new price is current and the old figure appears nowhere. They are written to fail while the copy is stale, which is the point; update them last so they gate the rest.
99+
100+
The site is a deploy, not a release. It does not wait for App Review, and it
101+
must not be held back to ship alongside an app update: that would leave the site
102+
quoting a price lower than the store actually charges for the length of a review
103+
cycle. Deploy it as soon as the live price is confirmed.
72104

73105
## 2. Privacy Policy
74106
- **URL:** [https://patternsocd.com/privacy](https://patternsocd.com/privacy)

app-store/patterns-screenshots/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,6 @@ node render.js
3535
4. Private Insights overview showing practice progress, mood, urge intensity, ERP sessions, and consistency.
3636
5. Full-screen private journal editor with a reflective entry and formatting controls.
3737
6. Settings screen showing Privacy & Safety, app lock, local-only records, data deletion, exports, and Pro controls.
38-
7. Standalone Patterns Pro value card with the one-time $14.99 unlock and six recovery tools.
38+
7. Standalone Patterns Pro value card with the one-time $39.99 unlock and six recovery tools.
3939

4040
Feature graphic: Patterns branding beside a card recommending a two-minute delay, with the line “A quieter way through OCD.”

app-store/patterns-screenshots/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ <h1>Move beyond tracking.<br /><em>Practise recovery.</em></h1>
286286
</header>
287287
<div class="pro-showcase">
288288
<article class="pro-price-card">
289-
<div class="pro-card-head"><img src="../../assets/logo.png" alt="" /><div><span>ONE-TIME PURCHASE</span><strong>$14.99</strong></div></div>
289+
<div class="pro-card-head"><img src="../../assets/logo.png" alt="" /><div><span>ONE-TIME PURCHASE</span><strong>$39.99</strong></div></div>
290290
<h2>The full recovery toolkit.<br />Yours for good.</h2>
291291
<p>Move beyond tracking with practical tools for ERP, uncertainty, and response prevention.</p>
292292
<div class="pro-feature-grid">

ios/Patterns.storekit

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"nonRenewingSubscriptions" : [ ],
88
"products" : [
99
{
10-
"displayPrice" : "14.99",
10+
"displayPrice" : "39.99",
1111
"familyShareable" : false,
1212
"internalID" : "1000000001",
1313
"localizations" : [

website/src/content/blog/between-therapy-sessions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ Proper OCD treatment is not equally available to everyone. Therapy is expensive
114114

115115
Entries stay on the device. No account, no sign-up wall, nothing sent to a server for the app to be useful. I made that decision for myself first, because I knew I would not write honestly otherwise.
116116

117-
On price: the core features are free. Pro is a one-time purchase with lifetime access, $14.99 through the launch period and $39.99 from 15 August 2026, still one time, still lifetime. There is no subscription and there is not going to be one. If you already own Pro, nothing changes and you are never charged again.
117+
On price: the core features are free. Pro is a one-time purchase with lifetime access, $39.99, one payment, kept for good. There is no subscription and there is not going to be one. If you already own Pro, nothing changes and you are never charged again.
118118

119119
The reasoning is not complicated. I wanted the basic tools to stay accessible without another monthly bill attached to someone's recovery, and I wanted Pro to stay a single payment, because I know how it feels when every useful mental health tool turns into a recurring charge. Pro is what funds continuing to build the thing.
120120

website/src/lib/data/copy-guards.test.ts

Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { readFileSync } from 'node:fs';
1+
import { readdirSync, readFileSync } from 'node:fs';
22
import { resolve } from 'node:path';
33
import { describe, expect, it } from 'vitest';
44
import { faqs } from './faq';
@@ -22,6 +22,14 @@ const roadmapSource = readFileSync(
2222
'utf-8'
2323
);
2424

25+
// Blog posts are the third place pricing gets described, and the least
26+
// reviewed. A draft of one of them announced a $14.99/year subscription that
27+
// was never built, and it only escaped publication because a deploy failed.
28+
const blogDir = resolve(process.cwd(), 'src/content/blog');
29+
const blogPosts = readdirSync(blogDir)
30+
.filter((name) => name.endsWith('.md'))
31+
.map((name) => ({ name, body: readFileSync(resolve(blogDir, name), 'utf-8') }));
32+
2533
describe('copy style', () => {
2634
it('keeps em dashes out of every FAQ answer', () => {
2735
for (const faq of faqs) {
@@ -47,22 +55,55 @@ describe('pricing claims', () => {
4755
expect(pricingAnswer.toLowerCase()).not.toContain('free to download and use, with no');
4856
});
4957

50-
// Until the store price actually changes, the site must quote what people are
51-
// charged today and describe $19.99 as upcoming. Stating $19.99 flatly would
52-
// put a price nobody pays into the page and into FAQPage structured data.
58+
// The rise to $39.99 was applied on 16 August 2026, so it is now simply the
59+
// price. The FAQ answers are rendered into FAQPage JSON-LD, so a stale figure
60+
// here is a structured-data claim search engines repeat as the current price.
5361
it('quotes the price people are charged today', () => {
54-
expect(pricingAnswer).toContain('$14.99');
62+
expect(pricingAnswer).toContain('$39.99');
63+
});
64+
65+
// The rise has landed, so nothing may still frame it as upcoming, and nothing
66+
// may dangle a buy-before-the-deadline discount that can no longer be taken.
67+
it('does not present the price rise as still to come', () => {
68+
for (const source of [pricingAnswer, roadmapSource]) {
69+
expect(source).not.toMatch(/goes to \$39\.99|rises to \$39\.99|going to \$39\.99/);
70+
expect(source).not.toMatch(/before the \d{1,2}th|before then you keep/);
71+
}
5572
});
5673

57-
it('describes the rise to $19.99 as still to come, not as current', () => {
58-
expect(pricingAnswer).toContain('$19.99');
59-
expect(pricingAnswer).toMatch(/rises to \$19\.99|going to \$19\.99/);
60-
expect(pricingAnswer).not.toMatch(/It is \$19\.99|costs \$19\.99/);
74+
// Pro is a one-time purchase and is staying that way. An earlier draft of the
75+
// blog announced a $14.99/year subscription that was never built; this stops
76+
// that claim reappearing anywhere the site can publish it.
77+
it('never advertises a subscription', () => {
78+
const recurring =
79+
/\$\d+(\.\d\d)?\s*(per year|\/ ?year|a year|per month|\/ ?month|a month)/i;
80+
81+
for (const faq of faqs) {
82+
expect(faq.answer, `answer to: ${faq.question}`).not.toMatch(recurring);
83+
}
84+
expect(roadmapSource, 'roadmap').not.toMatch(recurring);
85+
for (const post of blogPosts) {
86+
expect(post.body, `blog post: ${post.name}`).not.toMatch(recurring);
87+
}
88+
});
89+
90+
it('finds blog posts to scan', () => {
91+
expect(blogPosts.length).toBeGreaterThan(0);
92+
});
93+
94+
// $14.99 is retired everywhere, not merely demoted from "future price" to
95+
// "current price". Leaving it anywhere quotable is how the site ends up
96+
// advertising less than the store actually charges.
97+
it('does not leave the retired $14.99 figure anywhere', () => {
98+
expect(pricingAnswer, 'FAQ pricing answer').not.toContain('$14.99');
99+
expect(roadmapSource, 'roadmap').not.toContain('$14.99');
100+
for (const post of blogPosts) {
101+
expect(post.body, `blog post: ${post.name}`).not.toContain('$14.99');
102+
}
61103
});
62104

63-
// The roadmap announces the change, so it may say $19.99, but it must never
64-
// present $14.99 as the price going forward.
65-
it('does not present $14.99 as the future price on the roadmap', () => {
66-
expect(roadmapSource).not.toMatch(/moves to \$14\.99|will be \$14\.99/);
105+
it('does not leave the retired $19.99 figure anywhere', () => {
106+
expect(pricingAnswer).not.toContain('$19.99');
107+
expect(roadmapSource).not.toContain('$19.99');
67108
});
68109
});

website/src/lib/data/faq.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export const faqs: FaqItem[] = [
4343
{
4444
question: 'How much does Patterns cost?',
4545
answer:
46-
'Patterns is free to download, and the journal, the OCD log, the Y-BOCS self-check, guided ERP, the compulsion delay tool, and the emergency toolkit are all free with no time limit. Patterns Pro is an optional one-time unlock for the deeper ERP tools: exposure ladders, exposure materials, urge surfing, response prevention, structured programs, and the extra insights. Pro is currently $14.99 on iPhone and Android, and rises to $19.99 when the next update lands. If you buy it before then you keep it at the lower price and are never charged again. One payment, no subscription, nothing to renew. Patterns is open source, and optional tips and GitHub Sponsors are there if you would like to contribute beyond that.'
46+
'Patterns is free to download, and the journal, the OCD log, the Y-BOCS self-check, guided ERP, the compulsion delay tool, and the emergency toolkit are all free with no time limit. Patterns Pro is an optional one-time unlock for the deeper ERP tools: exposure ladders, exposure materials, urge surfing, response prevention, structured programs, and the extra insights. Pro is $39.99 in the US on iPhone and Android, with local pricing elsewhere. It is one payment, never a subscription and nothing to renew. If you already own Pro, nothing changes and you are never charged again. Patterns is open source, and optional tips and GitHub Sponsors are there if you would like to contribute beyond that.'
4747
},
4848
{
4949
question: 'Can I back up or move my data?',

website/src/routes/+page.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
category: 'One-time purchase',
6868
priceSpecification: {
6969
'@type': 'PriceSpecification',
70-
price: '14.99',
70+
price: '39.99',
7171
priceCurrency: 'USD'
7272
}
7373
}

website/src/routes/roadmap/+page.svelte

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@
5959
icon: Eraser,
6060
title: 'Clearing a day you wrote by mistake',
6161
body: 'If an entry went onto the wrong date, open that day, tap the bin, and confirm. The day goes blank again and nothing else is touched.'
62+
},
63+
{
64+
icon: Crown,
65+
title: 'What Pro costs',
66+
body: 'Pro is now $39.99 in the US, with local pricing elsewhere. Still one payment, still lifetime, still no subscription. If you already own Pro, nothing changes and you pay nothing again.'
6267
}
6368
];
6469
@@ -122,12 +127,7 @@
122127
icon: HeartHandshake,
123128
title: 'Kinder wording when something is missing',
124129
body: 'A pass over every screen that can stop you saving, so the message reads like a nudge rather than a telling-off. Nobody needs that from an app about OCD.'
125-
},
126-
{
127-
icon: Crown,
128-
title: 'What Pro costs from the next update',
129-
body: 'Pro on your phone moves to $19.99, paid once. If you already own Pro, nothing changes and you pay nothing again.'
130-
},
130+
}
131131
];
132132
133133
const exploring = [

0 commit comments

Comments
 (0)