Skip to content

feat: add a subscription clock offset to dev settings - #798

Merged
jvsena42 merged 9 commits into
masterfrom
feat/demo-clock-subscriptions
Oct 2, 2026
Merged

jvsena42 merged 9 commits into
masterfrom
feat/demo-clock-subscriptions

Conversation

@ovitrif

@ovitrif ovitrif commented Sep 24, 2026 •

Copy link
Copy Markdown
Collaborator

Twin: synonymdev/bitkit-android#1341

Description

  • Adds Dev Settings › PAYKIT › "Subscription clock offset (days)" (Off, 1, 7, 30, 31, 62, 365), so a debug build can test a subscription renewal without waiting a billing period
  • Applies the offset to subscription scheduling only: due periods, renewal dates, proposal visibility and due notifications; proposal start dates, acceptance times, one-time requests, invoices and payments keep real time
  • Accepting a subscription records the real acceptance time, so accepting with the offset on still makes the first period due
  • Reschedules pending due notifications when the offset changes and notifies the latest period per subscription that becomes due because of the jump
  • Refreshes subscriptions as soon as the offset changes instead of waiting for the next periodic refresh
  • Keeps the offset Off by default and ignores it outside debug builds, so release behaviour is unchanged

Out of Scope

  • Device clock: unchanged; moving the simulator clock breaks the Pubky sessions, which is why the offset lives in the app
  • Release builds: the row and the offset are unavailable there

Design

N/A — no design available.

Preview

Next period due Two paid periods Dev Settings row
Recording
subscription-clock-renewal.mp4

QA Notes

Journeys

  • temporary second-billing-period.xml — with a 31-day offset the next period comes due as a Payment Request and the subscription detail lists two payments
second-billing-period.xml
<journey name="Second Billing Period">
  <description>
    The next period of a monthly subscription comes due without waiting a month. Dev Settings carries
    a subscription clock offset, in debug builds only, that moves subscription scheduling forward by a
    number of days: due periods, renewal dates, proposal visibility and due notifications. Proposals and
    acceptance keep real time, one-time requests, invoices and payments keep real time, and the offset is
    Off by default. Requires the active "Journey Sub" from review-and-subscribe.xml with its first
    period paid on acceptance, and both instances on a debug build.
  </description>
  <actions>
    <action>Launch the E2E Bitkit app on both instances with "Journey Sub" active and its first period paid, per review-and-subscribe.xml</action>
    <action>On the creator instance, open the menu, navigate to Settings (id "DrawerSettings"), then Dev Settings, and scroll to the PAYKIT section</action>
    <action>Verify the "Subscription clock offset (days)" row (id "SubscriptionClockOffset") reads "Off"</action>
    <action>Tap the row and pick 31 (id "SubscriptionClockOffset-31"); verify the row now reads "31"</action>
    <action>Repeat the two previous steps on the payer instance</action>
    <action>On the payer instance, return home and verify within about ten seconds that the next period arrives as a Payment Request for "Journey Sub" over 5,000 sats, opening the Swipe To Pay sheet or waiting in the bell (id "PaymentRequestsBell")</action>
    <action>Pay it and verify Bitcoin Sent</action>
    <action>On the creator instance, verify the payment arrives, as the Received Instant Bitcoin sheet or a new row in Activity</action>
    <action>On the payer instance, open the drawer menu, tap Subscriptions and tap the "Journey Sub" row</action>
    <action>Verify the PAYMENTS section lists two payments, today's and one dated a month later, and that RENEWS moved a further month ahead</action>
    <action>On both instances, open Dev Settings again and set the subscription clock offset back to Off (id "SubscriptionClockOffset-0"); verify the row reads "Off"</action>
  </actions>
</journey>

Manual Tests

N/A

Automated Checks

  • added SubscriptionClockTests.swift — presets, clamping and the offset applied to subscription dates
  • updated PaykitPaymentRequestServiceTests.swift — the offset makes the next billing period due, accepting with the offset on keeps the first period due, proposals keep real-time start dates, and due notifications are rescheduled, raised once per subscription, served for every subscription and kept within the cap when the offset changes
  • ran SubscriptionClockTests, PaykitPaymentRequestServiceTests, PaykitSubscriptionProposalTests, PaykitPaymentRequestPollingScheduleTests and PaykitPaymentProofServiceTests on the current head: 203 tests passed

Dev Settings > PAYKIT > Demo clock offset (days) moves Paykit subscription
scheduling forward so a recorded demo can show a renewal without waiting a
billing period. It covers proposal anchors and expiry, acceptance, due periods,
renewal dates and due notifications. One-time requests, invoices, payments and
allowance checks keep real time. Off by default and ignored outside Debug.
@ovitrif ovitrif added this to the 2.7.0 milestone Oct 1, 2026
@ovitrif ovitrif changed the title feat: add a demo clock for subscription renewals feat: add a subscription clock offset to dev settings Oct 1, 2026
@ovitrif
ovitrif marked this pull request as ready for review October 1, 2026 20:00
@ovitrif
ovitrif requested review from jvsena42 and pwltr October 1, 2026 20:00
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-01T20:04:05.310590Z ab595f9 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@ovitrif

ovitrif commented Oct 1, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed ab595f9: merged master, renamed the demo clock to the subscription clock offset, and addressed the review findings from the Android twin (synonymdev/bitkit-android#1341) that apply here.

  • Acceptance records the real time, so accepting with the offset on keeps the first period due
  • Proposals publish real-time start and anchor dates, and the offset stays local to scheduling
  • Due notifications fire at the offset-adjusted time, are rescheduled when the offset changes, and a period that becomes due from the jump is notified
  • Changing the offset refreshes subscriptions right away

Unit tests: the simulator build for testing succeeds on this head; the tests have not run on it yet because no simulator host with free space was available. The earlier head passed 193 tests across the subscription suites. The recording follows once a host is available.

@jvsena42 @pwltr ready for review.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ab595f9a62

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Bitkit/Services/PaykitSubscription.swift Outdated
Comment thread Bitkit/Services/PaykitSubscription.swift Outdated
@greptile-apps

greptile-apps Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 2/5

[Medium risk] Adds a dev-only time offset for subscription testing.

The PR should not merge until offset changes reliably refresh due requests and notify periods made due by a jump.

Findings

  1. P1 Offset change can miss refresh ▶
  2. P1 Concurrent sync loses due alerts ▶
  3. P1 Final period misses due alert ▶

Summary

The PR adds a debug-only subscription clock offset and applies it to renewal calculations, presentation, and notification scheduling while retaining real time for payments and acceptance.

  • Adds a developer-settings preset menu and immediate-refresh attempt.
  • Recomputes subscription periods and notification fire dates using the offset.
  • Adds clock and subscription-request tests.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Dev Settings offset] --> B[SubscriptionClock]
  A --> C[Manager refresh]
  B --> C
  C --> D[Due periods and pending requests]
  C --> E[Notification scheduler]
  E --> F[Real-time notification triggers]
Loading

Reviews (1) · Last reviewed commit: "docs: describe what the subscription clo..."

Comment thread Bitkit/Views/Settings/DevSettingsView.swift Outdated
Comment thread Bitkit/Services/PaykitSubscription.swift Outdated
Comment thread Bitkit/Services/PaykitSubscription.swift Outdated
@ovitrif

ovitrif commented Oct 1, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed 6657bb0 for the Codex and Greptile findings; all five threads are replied to and resolved.

  • Due notifications raised by an offset jump are limited to the latest period per subscription and count toward the 32-notification cap
  • A subscription whose end the jump crosses still gets its due notification
  • Notifications scheduled before the jump for periods the clock has already passed are removed
  • A synchronization that is superseded no longer consumes the jump, so the newer one still notifies
  • Changing the offset waits for a refresh that already read the old clock and refreshes again

Unit tests on this head: 202 passed across SubscriptionClockTests, PaykitPaymentRequestServiceTests, PaykitSubscriptionProposalTests, PaykitPaymentRequestPollingScheduleTests and PaykitPaymentProofServiceTests, including the new notification and acceptance cases. The recording follows.

@ovitrif

ovitrif commented Oct 1, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed 9f38bf1: a test for the catch-up alerts across subscriptions, plus the recording and screenshots in the description.

  • Catch-up alerts pick the latest crossed period per subscription before the 32-notification cap, so one subscription cannot use up the cap; the new test covers a daily and an ending weekly subscription together
  • A subscription whose end the jump crosses still gets its alert (covered since 6657bb0)
  • Unit tests on this head: 203 passed across the subscription suites
  • Recorded on two simulators with fresh wallets on staging: first period paid, offset set to 31 on both, the next period came due as a Payment Request, was paid, and the subscription detail lists two payments

@jvsena42 jvsena42 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

One LOW inline (debug only). No HIGH/MEDIUM.

I checked every lead from the Android twin, synonymdev/bitkit-android#1341:

  • Acceptance is stored on real time (PaykitPaymentRequestService.swift:1672), and the restored fallback is real (:2024).
  • Published startsAt/anchor and proposal expiry stay on real time.
  • Notification fire dates are startsAt - offset, replaced in place under stable identifiers, so a change or reset produces no duplicates.
  • Catch-up is limited to (previousNow, now], one per subscription before the cap, and keeps the final period past endsAt.
  • A superseded sync does not consume the jump.
  • Periods paid under the offset appear in history.

No double pay. Ids derive from billingPeriod.startsAt, and recurringPending excludes proof-submitted, completed and in-flight ids. Recurring requests have no expiresAt, so the real-clock expiry check cannot block an offset-due period. Gating holds: SubscriptionClock.isAvailable = Env.isDebug, so a stored offset is ignored in TestFlight/App Store builds even with dev settings unlocked.

Device gate: n/a — dev-settings-only change with no journey files.

Comment thread Bitkit/Views/Subscriptions/SubscriptionsView.swift
@ovitrif
ovitrif requested a review from jvsena42 October 2, 2026 09:41
@ovitrif

ovitrif commented Oct 2, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed 04258ea for @jvsena42's review: the subscription review sheet now passes the real time as acceptedAt for its period preview and its timer, so under an offset it names the period that accepting pays (a monthly proposal starting Jan 2 with offset 31 shows "ends Feb 2", not "Mar 2"). The thread is resolved.

Unit tests on this head: 204 passed across the subscription suites, including the new review-period test.

@jvsena42 jvsena42 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Delta since 9f38bf176 (04258ea): no findings. The review-sheet LOW is fixed.

  • paymentDueOnAcceptance(at:acceptedAt:) now uses periods(through: shifted, acceptedAt: real), which matches accept (real acceptance at PaykitPaymentRequestService.swift:1672, earliest pending period). This covers the swipe title, the "first period ends" label and the transition date.
  • The new test covers it.
  • With the offset Off, acceptedAt defaults to date, so behaviour is unchanged.

The Android twin synonymdev/bitkit-android#1341 has the same fix.

Device gate: n/a — dev-settings-only change with no journey files.

@jvsena42 jvsena42 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Approved!

OBS: Could have some permanent AI instructions to improve discoverability by AI

@jvsena42
jvsena42 enabled auto-merge October 2, 2026 10:52
@jvsena42
jvsena42 merged commit 28243f1 into master Oct 2, 2026
17 checks passed
@jvsena42
jvsena42 deleted the feat/demo-clock-subscriptions branch October 2, 2026 11:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants