Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
2de9fac
feat: add paykit allowance admission and auto-pay executor
ovitrif Sep 24, 2026
621fcda
feat: add allowances tab, set allowance sheet and auto-pay wiring
ovitrif Sep 24, 2026
6eafd42
fix: attribute auto-paid activity and round allowance estimates
ovitrif Sep 24, 2026
f1291e1
fix: keep pre-allowance requests manual and log allowance decisions
ovitrif Sep 24, 2026
8d04d1f
test: cover allowance admission, limits and restart recovery
ovitrif Sep 24, 2026
44ae954
fix: count allowance months from the original anchor
ovitrif Sep 24, 2026
460e7ad
test: pass the proof allowance id in existing paykit fixtures
ovitrif Sep 24, 2026
c725de9
fix: leave wallets without an allowance ledger untouched at launch
ovitrif Sep 24, 2026
d577b84
fix: hold automatic payments until the payee's next list and usable c…
ovitrif Sep 24, 2026
80dde8e
docs: add the allowance journeys
ovitrif Sep 24, 2026
b806944
chore: add changelog fragment
ovitrif Sep 24, 2026
dbe7fd4
fix: adapt allowances to the shared paykit identity
ovitrif Oct 1, 2026
87d7996
fix: finish an allowance payment once it starts
ovitrif Oct 1, 2026
a593f0e
fix: keep an auto-accepted request recoverable as a manual payment
ovitrif Oct 1, 2026
8f0449c
Merge remote-tracking branch 'origin/codex/paykit-shared-runtime-loca…
ovitrif Oct 1, 2026
8cac27f
fix: keep a covered request off the send sheet until its automatic pa…
ovitrif Oct 2, 2026
1cde24a
fix: stop the request presentation from spinning on a request the all…
ovitrif Oct 2, 2026
a19ea87
Merge remote-tracking branch 'origin/codex/paykit-shared-runtime-loca…
ovitrif Oct 2, 2026
fcc9e86
Merge remote-tracking branch 'origin/codex/paykit-shared-runtime-loca…
ovitrif Oct 2, 2026
9993a22
Merge remote-tracking branch 'origin/codex/paykit-shared-runtime-loca…
ovitrif Oct 2, 2026
0f4ec4e
test: pin that allowance windows ignore the subscription clock offset
ovitrif Oct 2, 2026
af2e92c
Merge remote-tracking branch 'origin/codex/paykit-shared-runtime-loca…
ovitrif Oct 2, 2026
33dcf4b
Merge remote-tracking branch 'origin/codex/paykit-shared-runtime-loca…
ovitrif Oct 2, 2026
cdb664e
Merge remote-tracking branch 'origin/codex/paykit-shared-runtime-loca…
ovitrif Oct 2, 2026
6658b0f
Merge remote-tracking branch 'origin/codex/paykit-shared-runtime-loca…
ovitrif Oct 2, 2026
7eda1ec
Merge remote-tracking branch 'origin/codex/paykit-shared-runtime-loca…
ovitrif Oct 2, 2026
6a97572
Merge remote-tracking branch 'origin/codex/paykit-shared-runtime-loca…
ovitrif Oct 2, 2026
e53eaae
Merge remote-tracking branch 'origin/codex/paykit-shared-runtime-loca…
ovitrif Oct 2, 2026
32d00d7
Merge remote-tracking branch 'origin/codex/paykit-shared-runtime-loca…
ovitrif Oct 2, 2026
bb6b941
Merge remote-tracking branch 'origin/codex/paykit-shared-runtime-loca…
ovitrif Oct 2, 2026
8eac259
Merge remote-tracking branch 'origin/codex/paykit-shared-runtime-loca…
ovitrif Oct 2, 2026
fe1ef54
Merge remote-tracking branch 'origin/codex/paykit-shared-runtime-loca…
ovitrif Oct 2, 2026
20bb4d5
Merge remote-tracking branch 'origin/codex/paykit-shared-runtime-loca…
ovitrif Oct 2, 2026
426403f
Merge remote-tracking branch 'origin/codex/paykit-shared-runtime-loca…
ovitrif Oct 2, 2026
2073233
Merge remote-tracking branch 'origin/codex/paykit-shared-runtime-loca…
ovitrif Oct 2, 2026
ce67537
Merge remote-tracking branch 'origin/master' into feat/paykit-allowances
ovitrif Oct 19, 2026
905a345
fix: keep allowances to bitcoin requests and back up their proofs
ovitrif Oct 19, 2026
1b44a7d
fix: keep manual non-bitcoin payments out of the allowance ledger
ovitrif Oct 19, 2026
8edbc4f
fix: leave bitcoin requests that offer a conversion manual
ovitrif Oct 19, 2026
ea65e76
Merge remote-tracking branch 'origin/master' into feat/paykit-allowances
ovitrif Oct 19, 2026
47ed839
fix: count automatic on-chain allowance payments only when accepted
ovitrif Oct 19, 2026
466e1d8
feat: ask for the payment PIN before granting or ending an allowance
ovitrif Oct 19, 2026
e2c84e4
fix: keep uncertain automatic on-chain sends from being paid again
ovitrif Oct 19, 2026
efb6ffe
test: port the allowance PIN journey from android
ovitrif Oct 19, 2026
ab43260
fix: pay automatic allowance requests with the manual flow's guards
ovitrif Oct 19, 2026
141703d
fix: never pay an allowance without both amount limits
ovitrif Oct 19, 2026
10abf2e
fix: pay allowances automatically over lightning only
ovitrif Oct 19, 2026
b8404dc
fix: drop allowance proposals the payer can't see limits for
ovitrif Oct 19, 2026
bf9583a
Merge remote-tracking branch 'origin/master' into feat/paykit-allowances
ovitrif Oct 19, 2026
95491a7
fix: cap routing fees on automatic allowance payments
ovitrif Oct 19, 2026
b18cb79
fix: drop allowance proposals whose limits don't parse to sats
ovitrif Oct 19, 2026
88ed72d
Merge remote-tracking branch 'origin/master' into feat/paykit-allowances
ovitrif Oct 20, 2026
2e7c71d
chore: format allowance code and fix swift warnings
ovitrif Oct 20, 2026
ea880c0
fix: answer only the shown contact's allowance proposal
ovitrif Oct 20, 2026
407c225
fix: show each contact's allowance proposal even when ids match
ovitrif Oct 20, 2026
a844c40
docs: name the allowance row by its entry id in journeys
ovitrif Oct 20, 2026
b059b8e
fix: count each contact's automatic payments only toward its own allo…
ovitrif Oct 20, 2026
b005f8f
Merge remote-tracking branch 'origin/master' into feat/paykit-allowances
ovitrif Oct 20, 2026
da90209
fix: let requests over the allowance limits ask while a channel recon…
ovitrif Oct 21, 2026
7cb14fb
fix: tell the payer a held request is already being paid
ovitrif Oct 21, 2026
fd4c701
feat: translate the allowance strings into every supported language
ovitrif Oct 21, 2026
3e4c612
fix: raise limit reached only when a request is over a limit
ovitrif Oct 21, 2026
efe4aa2
fix: keep automatic and manual payments of one request apart
ovitrif Oct 21, 2026
69da189
fix: free the payee's payment list when an automatic payment fails
ovitrif Oct 21, 2026
6f74f1a
fix: release the payment list of an attempt reconciliation already fa…
ovitrif Oct 21, 2026
98824b6
fix: pay bitcoin requests that also price a usdt rail automatically
ovitrif Oct 21, 2026
723cdf5
fix: present a held request as soon as allowances settle
ovitrif Oct 21, 2026
4a1e391
fix: release a consumed payment list only once
ovitrif Oct 21, 2026
d500fab
fix: never roll back a payment list a settlement already released
ovitrif Oct 21, 2026
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
84 changes: 84 additions & 0 deletions Bitkit/AppScene.swift
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ enum IncomingPaykitPaymentRequestPresentationDispatcher {
sheets: SheetViewModel,
canRetryPreparation: Bool,
handleSubscriptionNotification: () async -> Void,
allowanceProposal: () -> PaykitAllowanceEntry? = { nil },
presentPaymentRequest: () async -> Void
) async {
guard sheets.activeSheetConfiguration == nil || canRetryPreparation,
Expand All @@ -219,6 +220,10 @@ enum IncomingPaykitPaymentRequestPresentationDispatcher {
sheets.showSheet(.subscription, data: SubscriptionSheetItem(route: .review(subscription)))
return
}
if let allowance = allowanceProposal() {
sheets.showSheet(.subscription, data: SubscriptionSheetItem(route: .allowanceReview(allowance)))
return
}
await presentPaymentRequest()
}

Expand Down Expand Up @@ -359,6 +364,7 @@ struct AppScene: View {
@State private var receivedPaymentBackfillCache = PaykitReceivedPaymentBackfillCache(
activityChanges: CoreService.shared.activity.activitiesChangedPublisher
)
@State private var paykitAllowanceManager = PaykitAllowanceManager()

@State private var hideSplash = false
@State private var removeSplash = false
Expand Down Expand Up @@ -594,6 +600,7 @@ struct AppScene: View {
.environment(wallet.usdtWallet)
.environment(calculatorInputManager)
.environment(paykitPaymentRequestManager)
.environment(paykitAllowanceManager)
}

private var paykitEventContent: some View {
Expand All @@ -606,6 +613,7 @@ struct AppScene: View {
guard let session = pubkyProfile.automaticPaykitSession,
PubkyPublicKeyFormat.matches(pk, session.publicKey)
else { return }
await paykitAllowanceManager.activate(identity: pk)
await handlePendingPaykitSubscriptionNotification()
guard pubkyProfile.automaticPaykitSession == session else { return }
try? await contactsManager.loadContacts(for: pk)
Expand All @@ -624,6 +632,7 @@ struct AppScene: View {
} else if authState == .idle {
contactsManager.reset()
paykitPaymentRequestManager.clear()
paykitAllowanceManager.deactivate()
}
}
.onReceive(contactsManager.savedContactsChangedPublisher) { contacts in
Expand Down Expand Up @@ -664,6 +673,9 @@ struct AppScene: View {
guard !Task.isCancelled else { return }
paykitProofRefresh.complete(request)
}
.onReceive(PaykitAllowanceExecutor.eventPublisher.receive(on: DispatchQueue.main)) { event in
handlePaykitAllowanceEvent(event)
}
.onReceive(PaykitPaymentProofService.onchainPaymentResolutionPublisher) { resolution in
Task { @MainActor in
app.retainPaykitOnchainPaymentResolution(resolution, identity: pubkyProfile.publicKey)
Expand Down Expand Up @@ -701,6 +713,12 @@ struct AppScene: View {
Task { await presentNextIncomingPaykitItem() }
}
}
.onChange(of: paykitAllowanceManager.isUpdating) { _, isUpdating in
// A request held back while allowances were updating is presented as soon as they settle.
if !isUpdating {
Task { await presentNextIncomingPaykitItem() }
}
}
.onChange(of: incomingPaykitPaymentRequestPresentationState) { previous, current in
handleIncomingPaykitPaymentRequestPresentationStateChange(from: previous, to: current)
}
Expand Down Expand Up @@ -1407,6 +1425,19 @@ struct AppScene: View {
let refreshed = await paykitPaymentRequestManager.refresh(
mode: mode, forceFresh: forceFresh, messagePriority: messagePriority, canRefresh: canRefresh
)
// Deleting the profile withdraws the payment lists an automatic payment would consume.
if canRefresh(), pubkyProfile.automaticPaykitSession == session, !pubkyProfile.isDeletingProfile {
await paykitAllowanceManager.refresh()
// A request whose manual sheet is already open stays with the user.
let manualRequestId = incomingPaymentRequestPreparation.flatMap { $0.ownsSheet(sheets) ? $0.request?.id : nil }
let handledAutomatically = await paykitAllowanceManager.processIncomingRequests(
paykitPaymentRequestManager.pendingRequests.filter { $0.id != manualRequestId }
)
let adoptedAcceptances = paykitPaymentRequestManager.reloadAcceptedRequestIds()
if handledAutomatically || adoptedAcceptances, canRefresh() {
await paykitPaymentRequestManager.refresh(mode: .stored, canRefresh: canRefresh)
}
}
guard pubkyProfile.authState == .authenticated,
PubkyPublicKeyFormat.matches(identity, pubkyProfile.publicKey)
else { return false }
Expand Down Expand Up @@ -1566,6 +1597,20 @@ struct AppScene: View {
else { return }
for request in requests {
guard paykitPaymentRequestManager.isCurrentPresentation(request) else { return }
if await paykitAllowanceManager.isAutomaticallyHandling(request) {
// The next refresh presents it if the allowance flow hands it back; presenting again now would spin on it.
if paykitPaymentRequestManager.requestedPresentationId == request.id {
app.toast(type: .info, title: t("subscriptions__allowance_payment_in_progress"))
paykitPaymentRequestManager.releaseRequestedPresentation(request)
}
shouldPresentNextRequest = false
continue
}
// A refresh or pass still running may hand this request to the allowance flow; a later round presents it.
if paykitAllowanceManager.isUpdating, paykitAllowanceManager.mayCover(request) {
shouldPresentNextRequest = false
continue
}
let preparation: IncomingPaykitPaymentRequestPreparation
if let current = incomingPaymentRequestPreparation, current.ownsSheet(sheets) {
guard current.request?.id == request.id else { continue }
Expand Down Expand Up @@ -1898,6 +1943,44 @@ struct AppScene: View {
await presentNextIncomingPaykitPaymentRequest()
}

private func handlePaykitAllowanceEvent(_ event: PaykitAllowanceEvent) {
switch event {
case let .paidAutomatically(counterparty, amountSats, paymentId):
let title = t("subscriptions__allowance_executed_title")
let description = t(
"subscriptions__allowance_executed_description",
variables: ["amount": AllowanceAmountText.fiat(sats: amountSats, currency: currency), "name": contactName(counterparty)]
)
PaykitAllowanceNotifier.post(title: title, body: description, fallback: {
app.toast(type: .lightning, title: title, description: description, accessibilityIdentifier: "AllowancePaidToast")
})
Task {
await paykitAllowanceManager.refresh()
do {
try await activity.setContact(counterparty, forPaymentId: paymentId)
} catch {
Logger.warn("Failed to set contact for an automatic allowance payment: \(error)", context: "AppScene")
}
}
case let .limitReached(counterparty, amountSats):
let title = t("subscriptions__allowance_limit_title")
let description = t(
"subscriptions__allowance_limit_description",
variables: ["amount": AllowanceAmountText.fiat(sats: amountSats, currency: currency), "name": contactName(counterparty)]
)
PaykitAllowanceNotifier.post(title: title, body: description, fallback: {
app.toast(type: .warning, title: title, description: description, accessibilityIdentifier: "AllowanceLimitToast")
})
case .ledgerChanged:
Task { await paykitAllowanceManager.refresh() }
}
}

private func contactName(_ publicKey: String) -> String {
contactsManager.contacts.first { PubkyPublicKeyFormat.matches($0.publicKey, publicKey) }?.displayName
?? PubkyPublicKeyFormat.displayTruncated(publicKey)
}

private func presentNextIncomingPaykitItem() async {
guard scenePhase == .active,
pubkyProfile.automaticPaykitSession != nil,
Expand All @@ -1908,6 +1991,7 @@ struct AppScene: View {
sheets: sheets,
canRetryPreparation: canRetryIncomingPaymentRequestPreparation,
handleSubscriptionNotification: handlePendingPaykitSubscriptionNotification,
allowanceProposal: paykitAllowanceManager.proposalForPresentation,
presentPaymentRequest: presentNextIncomingPaykitPaymentRequest
)
}
Expand Down
3 changes: 3 additions & 0 deletions Bitkit/Models/PaykitPaymentStateBackup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ struct PaykitPaymentStateBackup: Codable {
let proofData: String?
let conversionQuoteId: String?
let billingPeriod: Period?
let allowanceId: String?
let onchainAddress: String?
let onchainAmountSats: UInt64?
let onchainWalletId: String?
Expand All @@ -101,6 +102,7 @@ struct PaykitPaymentStateBackup: Codable {
proofData = proof.proofData
conversionQuoteId = proof.conversionQuoteId
billingPeriod = proof.billingPeriod.map { Period(startsAt: $0.sdkValue.startsAt, endsAt: $0.sdkValue.endsAt) }
allowanceId = proof.allowanceId
onchainAddress = proof.onchainAddress
onchainAmountSats = proof.onchainAmountSats
onchainWalletId = proof.onchainWalletId
Expand Down Expand Up @@ -129,6 +131,7 @@ struct PaykitPaymentStateBackup: Codable {
paymentEndpointIdentifier: paymentEndpointIdentifier,
kind: kind,
billingPeriod: period,
allowanceId: allowanceId,
paymentStarted: paymentStarted,
paymentIdentifier: paymentIdentifier,
proofData: proofData,
Expand Down
47 changes: 47 additions & 0 deletions Bitkit/Resources/Localization/ar.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -1580,3 +1580,50 @@
"wallet__receive_liquidity__label_additional" = "سيولة رصيد إنفاق إضافية";
"wallet__receive_cjit_error_max__title" = "الحد الأقصى لسعة الاستلام";
"wallet__receive_cjit_error_max__description" = "الحد الأقصى الذي يمكنك استلامه في رصيد الإنفاق الآن هو ₿ {amount}.";
"subscriptions__allowances" = "المخصصات";
"subscriptions__allowances_empty_headline" = "أعدّ\n<accent>المخصصات</accent>";
"subscriptions__allowances_empty_description" = "يمكنك تعيين حدود إنفاق لتلبية طلبات الدفع من جهات الاتصال الموثوقة تلقائيًا.";
"subscriptions__allowance_add" = "إضافة مخصص";
"subscriptions__allowance_title" = "المخصص";
"subscriptions__allowance_choose_contact" = "اختر جهة اتصال";
"subscriptions__allowance_no_contacts" = "أضف جهة اتصال أولًا. تغطي المخصصات طلبات الدفع من جهات اتصالك.";
"subscriptions__allowance_set_title" = "تعيين المخصص";
"subscriptions__allowance_set_explanation" = "وافق تلقائيًا على طلبات الدفع من {name} ضمن هذه الحدود:";
"subscriptions__allowance_payment_limit" = "حد الدفعة";
"subscriptions__allowance_monthly_allowance" = "المخصص الشهري";
"subscriptions__allowance_set_summary" = "ستُدفع الطلبات حتى {perPayment} تلقائيًا، بحد أقصى {monthly} شهريًا، دون سؤالك في كل مرة.";
"subscriptions__allowance_save" = "حفظ المخصص";
"subscriptions__allowance_monthly_limit" = "الحد الشهري";
"subscriptions__allowance_per_payment_short" = "{amount} للدفعة";
"subscriptions__allowance_per_payment" = "لكل دفعة";
"subscriptions__allowance_each_month" = "كل شهر";
"subscriptions__allowance_up_to" = "حتى {amount}";
"subscriptions__allowance_status_active" = "نشط";
"subscriptions__allowance_status_waiting" = "بانتظار الرد";
"subscriptions__allowance_status_needs_answer" = "بانتظار ردك";
"subscriptions__allowance_status_scheduled" = "غير نشط بعد";
"subscriptions__allowance_status_expired" = "منتهي الصلاحية";
"subscriptions__allowance_status_declined" = "مرفوض";
"subscriptions__allowance_status_conflicted" = "يحتاج إلى انتباه";
"subscriptions__allowance_status_ended" = "منتهٍ";
"subscriptions__allowance_paid_automatically" = "تم دفع {amount} تلقائيًا";
"subscriptions__allowance_paid_so_far" = "مدفوع تلقائيًا";
"subscriptions__allowance_offer_headline" = "{name} يعرض\n<accent>مخصصًا</accent>";
"subscriptions__allowance_request_headline" = "{name} يطلب\n<accent>مخصصًا</accent>";
"subscriptions__allowance_offer_explanation" = "ستدفع محفظة {name} طلباتك ضمن هذه الحدود دون السؤال في كل مرة. يمكن لأي منكما إنهاؤه.";
"subscriptions__allowance_request_explanation" = "ستدفع محفظتك طلبات {name} ضمن هذه الحدود دون سؤالك في كل مرة. يمكن لأي منكما إنهاؤه.";
"subscriptions__allowance_accept" = "قبول";
"subscriptions__allowance_decline" = "رفض";
"subscriptions__allowance_swipe_end" = "اسحب لإنهاء المخصص";
"subscriptions__allowance_swipe_withdraw" = "اسحب للتراجع";
"subscriptions__allowance_detail_active_allower" = "تُدفع الطلبات ضمن هذه الحدود تلقائيًا. أي طلب يتجاوزها يحتاج إلى موافقتك أولًا.";
"subscriptions__allowance_detail_active_allowee" = "تُدفع طلباتك ضمن هذه الحدود تلقائيًا.";
"subscriptions__allowance_detail_ended" = "انتهى هذا المخصص. سيتطلب كل طلب موافقتك من جديد.";
"subscriptions__allowance_error_not_linked" = "جهة الاتصال هذه غير متصلة بعد. حاول مرة أخرى بعد قليل.";
"subscriptions__allowance_error_unavailable" = "هذا المخصص غير متاح حاليًا.";
"subscriptions__allowance_payment_in_progress" = "يجري دفع هذا الطلب بالفعل.";
"subscriptions__allowance_executed_title" = "تم تنفيذ الدفع";
"subscriptions__allowance_executed_description" = "أرسل الدفع التلقائي {amount} إلى {name}";
"subscriptions__allowance_limit_title" = "تم بلوغ الحد";
"subscriptions__allowance_limit_description" = "طلب بقيمة {amount} من {name} يتجاوز مخصصك. راجعه للدفع.";
"subscriptions__allowance_auto_paid" = "مدفوع تلقائيًا";
47 changes: 47 additions & 0 deletions Bitkit/Resources/Localization/ca.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -1764,3 +1764,50 @@
"usdt__deposit_tron_refund_note" = "Les devolucions de TRON poden requerir l'assistència d'un operador d'Orchestra.";
"usdt__deposit_refund_address_network" = "Adreça USDT a {network}";
"wallet__send_error_support" = "Contacta amb el suport";
"subscriptions__allowances" = "Assignacions";
"subscriptions__allowances_empty_headline" = "Configura\n<accent>assignacions</accent>";
"subscriptions__allowances_empty_description" = "Pots establir límits de despesa per pagar automàticament les sol·licituds de pagament de contactes de confiança.";
"subscriptions__allowance_add" = "Afegir assignació";
"subscriptions__allowance_title" = "Assignació";
"subscriptions__allowance_choose_contact" = "Triar contacte";
"subscriptions__allowance_no_contacts" = "Afegeix primer un contacte. Les assignacions cobreixen les sol·licituds de pagament dels teus contactes.";
"subscriptions__allowance_set_title" = "Establir assignació";
"subscriptions__allowance_set_explanation" = "Aprova automàticament les sol·licituds de pagament de {name} per sota d'aquests límits:";
"subscriptions__allowance_payment_limit" = "Límit per pagament";
"subscriptions__allowance_monthly_allowance" = "Assignació mensual";
"subscriptions__allowance_set_summary" = "Les sol·licituds de fins a {perPayment} es pagaran automàticament, fins a {monthly} al mes, sense preguntar-te cada vegada.";
"subscriptions__allowance_save" = "Desar assignació";
"subscriptions__allowance_monthly_limit" = "Límit mensual";
"subscriptions__allowance_per_payment_short" = "{amount} per pagament";
"subscriptions__allowance_per_payment" = "Per pagament";
"subscriptions__allowance_each_month" = "Cada mes";
"subscriptions__allowance_up_to" = "Fins a {amount}";
"subscriptions__allowance_status_active" = "Activa";
"subscriptions__allowance_status_waiting" = "Esperant resposta";
"subscriptions__allowance_status_needs_answer" = "Esperant la teva resposta";
"subscriptions__allowance_status_scheduled" = "Encara no activa";
"subscriptions__allowance_status_expired" = "Caducada";
"subscriptions__allowance_status_declined" = "Rebutjada";
"subscriptions__allowance_status_conflicted" = "Requereix atenció";
"subscriptions__allowance_status_ended" = "Finalitzada";
"subscriptions__allowance_paid_automatically" = "{amount} pagat automàticament";
"subscriptions__allowance_paid_so_far" = "Pagat automàticament";
"subscriptions__allowance_offer_headline" = "{name} t'ofereix\n<accent>una assignació</accent>";
"subscriptions__allowance_request_headline" = "{name} demana\n<accent>una assignació</accent>";
"subscriptions__allowance_offer_explanation" = "La cartera de {name} pagarà les teves sol·licituds dins d'aquests límits sense preguntar cada vegada. Qualsevol de vosaltres hi pot posar fi.";
"subscriptions__allowance_request_explanation" = "La teva cartera pagarà les sol·licituds de {name} dins d'aquests límits sense preguntar-te cada vegada. Qualsevol de vosaltres hi pot posar fi.";
"subscriptions__allowance_accept" = "Acceptar";
"subscriptions__allowance_decline" = "Rebutjar";
"subscriptions__allowance_swipe_end" = "Desplaça per finalitzar l'assignació";
"subscriptions__allowance_swipe_withdraw" = "Desplaça per retirar";
"subscriptions__allowance_detail_active_allower" = "Les sol·licituds dins d'aquests límits es paguen automàticament. Qualsevol import superior et demanarà confirmació abans.";
"subscriptions__allowance_detail_active_allowee" = "Les teves sol·licituds dins d'aquests límits es paguen automàticament.";
"subscriptions__allowance_detail_ended" = "Aquesta assignació ha finalitzat. Cada sol·licitud tornarà a demanar confirmació.";
"subscriptions__allowance_error_not_linked" = "Aquest contacte encara no està connectat. Torna-ho a provar d'aquí a un moment.";
"subscriptions__allowance_error_unavailable" = "Aquesta assignació no està disponible ara mateix.";
"subscriptions__allowance_payment_in_progress" = "Aquesta sol·licitud ja s'està pagant.";
"subscriptions__allowance_executed_title" = "Pagament executat";
"subscriptions__allowance_executed_description" = "El pagament automàtic ha enviat {amount} a {name}";
"subscriptions__allowance_limit_title" = "Límit assolit";
"subscriptions__allowance_limit_description" = "Una sol·licitud de {amount} de {name} supera la teva assignació. Revisa-la per pagar.";
"subscriptions__allowance_auto_paid" = "Pagat automàticament";
Loading
Loading