diff --git a/packages/backend-modules/access/graphql/resolvers/AccessGrant.js b/packages/backend-modules/access/graphql/resolvers/AccessGrant.js
index 25071f81aa..4add6ab4ec 100644
--- a/packages/backend-modules/access/graphql/resolvers/AccessGrant.js
+++ b/packages/backend-modules/access/graphql/resolvers/AccessGrant.js
@@ -1,3 +1,5 @@
+const moment = require('moment')
+
const campaignsLib = require('../../lib/campaigns')
const eventsLib = require('../../lib/events')
@@ -72,6 +74,10 @@ module.exports = {
return t('api/access/resolvers/AccessGrant/status/unclaimed')
}
+ if (grant.beginAt && moment(grant.beginAt).isAfter(moment())) {
+ return t('api/access/resolvers/AccessGrant/status/pending')
+ }
+
return t('api/access/resolvers/AccessGrant/status/valid')
},
events: (grant, args, { user: me, pgdb }) => {
diff --git a/packages/backend-modules/access/graphql/resolvers/User.js b/packages/backend-modules/access/graphql/resolvers/User.js
index 8dfd28f7d5..993d682b26 100644
--- a/packages/backend-modules/access/graphql/resolvers/User.js
+++ b/packages/backend-modules/access/graphql/resolvers/User.js
@@ -15,9 +15,19 @@ module.exports = {
const grants = await grantsLib.findByRecipient(user, { withPast, pgdb })
- debug('accessGrants', { user: user.id, grants: grants.length })
+ // include grants which were requested but begin in the future
+ // (campaigns with a grantBeginInterval); withPast already covers them
+ const pendingGrants = withPast
+ ? []
+ : await grantsLib.findPendingByRecipient(user, { pgdb })
+
+ debug('accessGrants', {
+ user: user.id,
+ grants: grants.length,
+ pendingGrants: pendingGrants.length,
+ })
- return grants
+ return [...pendingGrants, ...grants]
},
accessCampaigns: async (user, { withPast }, { user: me, pgdb }) => {
if (!Roles.userIsMeOrInRoles(user, me, PRIVILEDGED_ROLES)) {
diff --git a/packages/backend-modules/access/graphql/resolvers/_mutations/claimAccess.js b/packages/backend-modules/access/graphql/resolvers/_mutations/claimAccess.js
index cdacef964a..63260435ab 100644
--- a/packages/backend-modules/access/graphql/resolvers/_mutations/claimAccess.js
+++ b/packages/backend-modules/access/graphql/resolvers/_mutations/claimAccess.js
@@ -2,7 +2,7 @@ const debug = require('debug')('access:mutation:claimAccess')
const { ensureSignedIn } = require('@orbiting/backend-modules-auth')
-const { claim } = require('../../../lib/grants')
+const { claim, ensureUserHasNoActiveMembershipOrSubscription } = require('../../../lib/grants')
module.exports = async (
_,
@@ -10,7 +10,7 @@ module.exports = async (
{ req, user, pgdb, redis, t, mail },
) => {
ensureSignedIn(req)
- await ensureUserHasNoNewSubscription(user, pgdb, t)
+ await ensureUserHasNoActiveMembershipOrSubscription(user, pgdb, t)
debug('begin', { voucherCode, user: user.id })
const transaction = await pgdb.transactionBegin()
@@ -39,18 +39,3 @@ module.exports = async (
}
}
-async function ensureUserHasNoNewSubscription(user, pgdb, t) {
- const result = await pgdb.payments.subscriptions.findFirst(
- {
- userId: user.id,
- status: ['active', 'past_due', 'unpaid', 'paused'],
- },
- { fields: ['id'] },
- )
-
- if (result) {
- throw new Error(
- t('api/access/claim/can-not-claim-access-with-active-subscription'),
- )
- }
-}
diff --git a/packages/backend-modules/access/graphql/resolvers/_mutations/requestAccess.js b/packages/backend-modules/access/graphql/resolvers/_mutations/requestAccess.js
index a9e197628e..c84ebe7b0a 100644
--- a/packages/backend-modules/access/graphql/resolvers/_mutations/requestAccess.js
+++ b/packages/backend-modules/access/graphql/resolvers/_mutations/requestAccess.js
@@ -2,7 +2,7 @@ const debug = require('debug')('access:mutation:requestAccess')
const { ensureSignedIn } = require('@orbiting/backend-modules-auth')
-const { request } = require('../../../lib/grants')
+const { request, ensureUserHasNoActiveMembershipOrSubscription } = require('../../../lib/grants')
module.exports = async (
_,
@@ -10,6 +10,7 @@ module.exports = async (
{ req, user, pgdb, redis, t, mail },
) => {
ensureSignedIn(req)
+ await ensureUserHasNoActiveMembershipOrSubscription(user, pgdb, t)
debug('begin', { campaignId, user: user.id })
const transaction = await pgdb.transactionBegin()
@@ -37,3 +38,4 @@ module.exports = async (
throw e
}
}
+
diff --git a/packages/backend-modules/access/lib/AccessScheduler.js b/packages/backend-modules/access/lib/AccessScheduler.js
index a62d918608..7f8eb1f4b5 100644
--- a/packages/backend-modules/access/lib/AccessScheduler.js
+++ b/packages/backend-modules/access/lib/AccessScheduler.js
@@ -33,6 +33,7 @@ const init = async (context) => {
1000 * intervalSecs,
)
+ await activateDeferredGrants(t, pgdb, redis, mail)
await recommendations(t, pgdb, mail)
await expireGrants(t, pgdb, mail)
await followupGrants(t, pgdb, mail)
@@ -79,6 +80,31 @@ const init = async (context) => {
module.exports = { init }
+/**
+ * Activates begun grants whose activation was deferred by a campaign's
+ * grantBeginInterval: applies perks, member role and onboarding emails
+ * once beginAt is reached.
+ */
+const activateDeferredGrants = async (t, pgdb, redis, mail) => {
+ debug('activateGrants...')
+ for (const grant of await grantsLib.findUnactivatedDeferred(pgdb)) {
+ const transaction = await pgdb.transactionBegin()
+
+ try {
+ await grantsLib.activateGrant(grant, t, transaction, redis, mail)
+ await transaction.transactionCommit()
+ } catch (e) {
+ await transaction.transactionRollback()
+
+ console.error('activateDeferredGrants, grant failed', {
+ error: e,
+ grant: grant.id,
+ })
+ }
+ }
+ debug('activateGrants done')
+}
+
/**
* Sends recommendations on current grants (only for campaigns with active recommendations)
*/
diff --git a/packages/backend-modules/access/lib/grants.js b/packages/backend-modules/access/lib/grants.js
index bf033ecfa2..e29ce2c227 100644
--- a/packages/backend-modules/access/lib/grants.js
+++ b/packages/backend-modules/access/lib/grants.js
@@ -20,6 +20,28 @@ const VOUCHER_CODE_LENGTH = 5
const { REGWALL_TRIAL_CAMPAIGN_ID } = process.env
+const ensureUserHasNoActiveMembershipOrSubscription = async (user, pgdb, t) => {
+ if (await hasUserActiveMembership(user, pgdb)) {
+ throw new Error(
+ t('api/access/claim/can-not-claim-access-with-active-membership'),
+ )
+ }
+
+ const subscription = await pgdb.payments.subscriptions.findFirst(
+ {
+ userId: user.id,
+ status: ['active', 'past_due', 'unpaid', 'paused'],
+ },
+ { fields: ['id'] },
+ )
+
+ if (subscription) {
+ throw new Error(
+ t('api/access/claim/can-not-claim-access-with-active-subscription'),
+ )
+ }
+}
+
const evaluateConstraints = async (granter, campaign, email, t, pgdb) => {
const errors = []
@@ -188,21 +210,32 @@ const grant = async (granter, campaignId, email, message, t, pgdb, mail) => {
return grant
}
-const claim = async (voucherCode, payload, user, t, pgdb, redis, mail) => {
- const sanatizedVoucherCode = voucherCode.trim().toUpperCase()
-
- const grantByVoucherCode = await findByVoucherCode(sanatizedVoucherCode, {
- pgdb,
- })
+/**
+ * Applies a begun grant's side-effects: perks, member role, newsletter
+ * subscriptions and onboarding/claim notice emails. Runs inline on
+ * claim/request, or via scheduler once beginAt is reached for campaigns
+ * with a grantBeginInterval. Guarded by accessGrants.activatedAt.
+ */
+const activateGrant = async (grant, t, pgdb, redis, mail) => {
+ const now = moment()
+ const updated = await pgdb.public.accessGrants.update(
+ { id: grant.id, activatedAt: null },
+ { activatedAt: now, updatedAt: now },
+ )
- if (!grantByVoucherCode) {
- throw new Error(t('api/access/claim/404'))
+ if (updated < 1) {
+ debug('activateGrant, already activated', { id: grant.id })
+ return false
}
- const grant = await beginGrant(grantByVoucherCode, payload, user, pgdb)
- await eventsLib.log(grant, 'grant', pgdb)
-
- const { granter, recipient, campaign } = grant
+ const campaign =
+ grant.campaign || (await campaignsLib.findOne(grant.accessCampaignId, pgdb))
+ const granter =
+ grant.granter ||
+ (await pgdb.public.users.findOne({ id: grant.granterUserId }))
+ const recipient =
+ grant.recipient ||
+ (await pgdb.public.users.findOne({ id: grant.recipientUserId }))
const perks = await grantPerks(
grant,
@@ -218,10 +251,12 @@ const claim = async (voucherCode, payload, user, t, pgdb, redis, mail) => {
await Promise.map(perks, (perk) => {
if (perk) {
- const { name, ...other } = perk
+ const { name, eventLogExtend, ...other } = perk
grant.perks[perk.name] = other
- eventsLib.log(grant, `perk.${name}`, pgdb)
+ const event = `perk.${name}${eventLogExtend || ''}`
+
+ eventsLib.log(grant, event, pgdb)
}
})
}
@@ -234,7 +269,7 @@ const claim = async (voucherCode, payload, user, t, pgdb, redis, mail) => {
const subscribeToEditorialNewsletters =
campaign.config?.subscribeToEditorialNewsletters ||
- perks.some(({ settings }) => !!settings.subscribeToEditorialNewsletters) ||
+ perks.filter(Boolean).some(({ settings }) => !!settings.subscribeToEditorialNewsletters) ||
hasAddedMemberRole
await mail.enforceSubscriptions({
@@ -269,6 +304,44 @@ const claim = async (voucherCode, payload, user, t, pgdb, redis, mail) => {
pgdb,
)
+ debug('activateGrant', { id: grant.id })
+
+ return true
+}
+
+const claim = async (voucherCode, payload, user, t, pgdb, redis, mail) => {
+ const sanitizedVoucherCode = voucherCode.trim().toUpperCase()
+
+ const grantByVoucherCode = await findByVoucherCode(sanitizedVoucherCode, {
+ pgdb,
+ })
+
+ if (!grantByVoucherCode) {
+ throw new Error(t('api/access/claim/404'))
+ }
+
+ const grant = await beginGrant(grantByVoucherCode, payload, user, pgdb)
+ await eventsLib.log(grant, 'grant', pgdb)
+
+ if (moment(grant.beginAt).isAfter(moment())) {
+ // campaign has a grantBeginInterval; scheduler activates at beginAt
+ debug('claim, activation deferred', {
+ id: grant.id,
+ beginAt: grant.beginAt,
+ })
+
+ const { campaign, granter } = grant
+
+ const { enabled: inReviewEnabled = false } =
+ mailLib.getConfigEmails('recipient', 'in_review', campaign) || {}
+
+ if (inReviewEnabled) {
+ await mailLib.sendRecipientInReview(granter, campaign, user, grant, t, pgdb)
+ }
+ } else {
+ await activateGrant(grant, t, pgdb, redis, mail)
+ }
+
debug('grant', { grant })
return grant
@@ -342,62 +415,31 @@ const request = async (granter, campaignId, payload, t, pgdb, redis, mail) => {
await eventsLib.log(grant, 'request', pgdb)
- const perks = await grantPerks(grant, granter, campaign, t, pgdb, redis, mail)
- if (perks.length > 0) {
- grant.perks = {}
-
- await Promise.map(perks, (perk) => {
- if (perk) {
- const { name, eventLogExtend, ...other } = perk
- grant.perks[perk.name] = other
-
- const event = `perk.${name}${eventLogExtend || ''}`
-
- eventsLib.log(grant, event, pgdb)
- }
+ if (campaign.grantBeginInterval) {
+ // activation (perks, member role, onboarding email) is deferred to the
+ // scheduler once beginAt is reached
+ debug('request, activation deferred', {
+ id: grant.id,
+ beginAt: grant.beginAt,
})
- }
- const hasAddedMemberRole = await membershipsLib.addMemberRole(
- grant,
- granter,
- pgdb,
- )
+ const { enabled: inReviewEnabled = false } =
+ mailLib.getConfigEmails('recipient', 'in_review', campaign) || {}
- const subscribeToEditorialNewsletters =
- campaign.config?.subscribeToEditorialNewsletters ||
- perks.some(({ settings }) => !!settings.subscribeToEditorialNewsletters) ||
- hasAddedMemberRole
-
- await mail.enforceSubscriptions({
- userId: grant.granter.id,
- pgdb,
- subscribeToEditorialNewsletters,
- })
-
- const { enabled: onboardingEnabled = false } =
- mailLib.getConfigEmails('recipient', 'onboarding', campaign) || {}
-
- if (!(await hasUserActiveMembership(granter, pgdb)) || !!onboardingEnabled) {
- await mailLib.sendRecipientOnboarding(
- granter,
- campaign,
- granter,
- grant,
- t,
- pgdb,
- )
+ if (inReviewEnabled) {
+ await mailLib.sendRecipientInReview(
+ granter,
+ campaign,
+ granter,
+ grant,
+ t,
+ pgdb,
+ )
+ }
+ } else {
+ await activateGrant(grant, t, pgdb, redis, mail)
}
- await mailLib.sendGranterClaimNotice(
- granter,
- campaign,
- granter,
- grant,
- t,
- pgdb,
- )
-
return grant
}
@@ -489,7 +531,14 @@ const invalidate = async (grant, reason, t, pgdb, mail, requestUserId) => {
})
}
- if (!(await hasUserActiveMembership(recipient, pgdb)) && sendMail) {
+ const wasActivated =
+ !!grant.activatedAt || moment(grant.beginAt).isSameOrBefore(moment())
+
+ if (
+ wasActivated &&
+ !(await hasUserActiveMembership(recipient, pgdb)) &&
+ sendMail
+ ) {
const granter = await pgdb.public.users.findOne({
id: grant.granterUserId,
})
@@ -649,10 +698,13 @@ const findByVoucherCode = async (voucherCode, { pgdb }) => {
}
const regwallTrialStatus = async (user, { pgdb }) => {
- const trialGrant = await pgdb.public.accessGrants.findFirst({
- recipientUserId: user.id,
- accessCampaignId: REGWALL_TRIAL_CAMPAIGN_ID,
- }, {orderBy: {createdAt: 'desc'}})
+ const trialGrant = await pgdb.public.accessGrants.findFirst(
+ {
+ recipientUserId: user.id,
+ accessCampaignId: REGWALL_TRIAL_CAMPAIGN_ID,
+ },
+ { orderBy: { createdAt: 'desc' } },
+ )
if (!trialGrant) {
return null
}
@@ -665,7 +717,12 @@ const regwallTrialStatus = async (user, { pgdb }) => {
const isGrantActive = (grant) => {
const now = new Date()
- return !grant.invalidatedAt && !grant.revokedAt && grant.beginAt <= now && grant.endAt > now
+ return (
+ !grant.invalidatedAt &&
+ !grant.revokedAt &&
+ grant.beginAt <= now &&
+ grant.endAt > now
+ )
}
const findUnassignedByEmail = async (email, pgdb) => {
@@ -680,6 +737,38 @@ const findUnassignedByEmail = async (email, pgdb) => {
})
}
+const findUnactivatedDeferred = async (pgdb) => {
+ debug('findUnactivatedDeferred')
+ const campaignsWithBeginInterval = await pgdb.public.accessCampaigns.find({
+ 'grantBeginInterval !=': null,
+ })
+ if (!campaignsWithBeginInterval.length) {
+ return []
+ }
+ return pgdb.public.accessGrants.find({
+ 'accessCampaignId in': campaignsWithBeginInterval.map((c) => c.id),
+ 'beginAt <=': moment(),
+ activatedAt: null,
+ 'recipientUserId !=': null,
+ invalidatedAt: null,
+ revokedAt: null,
+ })
+}
+
+const findPendingByRecipient = async (recipient, { pgdb }) => {
+ debug('findPendingByRecipient', { recipient: recipient.id })
+
+ return pgdb.public.accessGrants.find(
+ {
+ recipientUserId: recipient.id,
+ 'beginAt >': moment(),
+ invalidatedAt: null,
+ revokedAt: null,
+ },
+ { orderBy: { createdAt: 'desc' } },
+ )
+}
+
const findInvalid = async (pgdb) => {
debug('findInvalid')
const now = moment()
@@ -699,8 +788,10 @@ const beginGrant = async (grant, payload, recipient, pgdb) => {
])
const now = moment()
- const beginAt = now.clone()
- const endAt = addInterval(beginAt, campaign.grantPeriodInterval)
+ const beginAt = campaign.grantBeginInterval
+ ? addInterval(now.clone(), campaign.grantBeginInterval)
+ : now.clone()
+ const endAt = addInterval(beginAt.clone(), campaign.grantPeriodInterval)
const updateFields = {
recipientUserId: recipient.id,
@@ -774,6 +865,7 @@ module.exports = {
grant,
claim,
request,
+ activateGrant,
revoke,
recommendations,
invalidate,
@@ -786,7 +878,11 @@ module.exports = {
regwallTrialStatus,
findUnassignedByEmail,
+ findUnactivatedDeferred,
+ findPendingByRecipient,
findInvalid,
findEmptyRecommendations,
findEmptyFollowup,
+
+ ensureUserHasNoActiveMembershipOrSubscription,
}
diff --git a/packages/backend-modules/access/lib/mail.js b/packages/backend-modules/access/lib/mail.js
index ea2f57e0b9..466dafdb6c 100644
--- a/packages/backend-modules/access/lib/mail.js
+++ b/packages/backend-modules/access/lib/mail.js
@@ -69,6 +69,23 @@ const sendRecipientOnboarding = async (
pgdb,
})
+const sendRecipientInReview = async (
+ granter,
+ campaign,
+ recipient,
+ grant,
+ t,
+ pgdb,
+) =>
+ sendMail(recipient.email, 'recipient', 'in_review', {
+ granter,
+ recipient,
+ campaign,
+ grant,
+ t,
+ pgdb,
+ })
+
const sendRecipientExpired = async (
granter,
campaign,
@@ -343,6 +360,9 @@ module.exports = {
// Onboarding
sendRecipientOnboarding,
+ // Notice that a request is being reviewed (campaign.grantBeginInterval)
+ sendRecipientInReview,
+
// Offboarding when access expired
sendRecipientExpired,
diff --git a/packages/backend-modules/access/lib/perks/subscribeToMailJourney.js b/packages/backend-modules/access/lib/perks/subscribeToMailJourney.js
index 44000866ea..9229fc2dfe 100644
--- a/packages/backend-modules/access/lib/perks/subscribeToMailJourney.js
+++ b/packages/backend-modules/access/lib/perks/subscribeToMailJourney.js
@@ -20,12 +20,18 @@ const give = async (
redis,
mail,
) => {
- if (!(settings?.audience)) {
- throw new Error(`Error while subscribing user to mailchimp journey ${settings?.audience}, valid audience settings are REGWALL_TRIAL and PROBELESEN`)
+ // audienceId (direct Mailchimp audience id from campaign config) takes
+ // precedence over the symbolic audience name mapped to env vars
+ const audienceId = settings?.audienceId || audiences[settings?.audience]
+
+ if (!audienceId) {
+ throw new Error(
+ `Error while subscribing user to mailchimp journey: provide settings.audienceId or settings.audience (REGWALL_TRIAL or PROBELESEN, requires the corresponding env var); got ${JSON.stringify(
+ settings,
+ )}`,
+ )
}
- const audienceId = audiences[settings.audience]
-
await mail.addUserToAudience({
user: recipient,
audienceId: audienceId,
diff --git a/packages/backend-modules/access/migrations/sqls/20260612120000-grant-begin-interval-down.sql b/packages/backend-modules/access/migrations/sqls/20260612120000-grant-begin-interval-down.sql
new file mode 100644
index 0000000000..894dacfc3f
--- /dev/null
+++ b/packages/backend-modules/access/migrations/sqls/20260612120000-grant-begin-interval-down.sql
@@ -0,0 +1,7 @@
+ALTER TABLE "accessCampaigns"
+ DROP COLUMN "grantBeginInterval"
+;
+
+ALTER TABLE "accessGrants"
+ DROP COLUMN "activatedAt"
+;
diff --git a/packages/backend-modules/access/migrations/sqls/20260612120000-grant-begin-interval-up.sql b/packages/backend-modules/access/migrations/sqls/20260612120000-grant-begin-interval-up.sql
new file mode 100644
index 0000000000..188edb1af8
--- /dev/null
+++ b/packages/backend-modules/access/migrations/sqls/20260612120000-grant-begin-interval-up.sql
@@ -0,0 +1,7 @@
+ALTER TABLE "accessCampaigns"
+ ADD COLUMN "grantBeginInterval" interval
+;
+
+ALTER TABLE "accessGrants"
+ ADD COLUMN "activatedAt" timestamp with time zone
+;
diff --git a/packages/backend-modules/mail/templates/access_granter_claim_notice_3months.html b/packages/backend-modules/mail/templates/access_granter_claim_notice_3months.html
new file mode 100644
index 0000000000..55cfe64ecf
--- /dev/null
+++ b/packages/backend-modules/mail/templates/access_granter_claim_notice_3months.html
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+ Guten Tag
+
+ Der von Ihnen vermittelte Republik-Zugang wurde soeben eingelöst.
+
+
+ Herzlichen Dank, dass Sie unser Magazin weiterempfohlen
+ haben und damit zur Zukunft der Republik beitragen!
+
+ Ihre Crew der Republik
+ |
+
+
+ |
+
+
+
+ Republik AG
+ Sihlhallenstrasse 1, CH-8004 Zürich
+ www.republik.ch
+ kontakt@republik.ch
+
+ |
+
+
+
+ |
+
+
+
+
+
diff --git a/packages/backend-modules/mail/templates/access_recipient_in_review.html b/packages/backend-modules/mail/templates/access_recipient_in_review.html
new file mode 100644
index 0000000000..4e9955886b
--- /dev/null
+++ b/packages/backend-modules/mail/templates/access_recipient_in_review.html
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+ Guten Tag
+
+ Vielen Dank für Ihre Anfrage! Wir haben sie erhalten und
+ prüfen sie derzeit.
+
+
+ Sobald die Prüfung abgeschlossen ist, melden wir uns wieder bei Ihnen.
+
+ Ihre Crew der Republik
+ |
+
+
+ |
+
+
+
+ Republik AG
+ Sihlhallenstrasse 1, CH-8004 Zürich
+ www.republik.ch
+ kontakt@republik.ch
+
+ |
+
+
+
+ |
+
+
+
+
+
diff --git a/packages/backend-modules/migrations/migrations/20260612120000-grant-begin-interval.js b/packages/backend-modules/migrations/migrations/20260612120000-grant-begin-interval.js
new file mode 100644
index 0000000000..d1eb51eda2
--- /dev/null
+++ b/packages/backend-modules/migrations/migrations/20260612120000-grant-begin-interval.js
@@ -0,0 +1,8 @@
+const run = require('../run.js')
+
+const dir = 'packages/backend-modules/access/migrations/sqls'
+const file = '20260612120000-grant-begin-interval'
+
+exports.up = (db) => run(db, dir, `${file}-up.sql`)
+
+exports.down = (db) => run(db, dir, `${file}-down.sql`)
diff --git a/packages/backend-modules/translate/translations.json b/packages/backend-modules/translate/translations.json
index 15a0f9e500..dce456b711 100755
--- a/packages/backend-modules/translate/translations.json
+++ b/packages/backend-modules/translate/translations.json
@@ -1404,6 +1404,10 @@
"key": "api/access/email/recipient/onboarding_gifted_membership/subject",
"value": "Willkommen an Bord"
},
+ {
+ "key": "api/access/email/recipient/in_review/subject",
+ "value": "Ihre Anfrage wird geprüft"
+ },
{
"key": "api/access/email/recipient/recommendations/subject",
"value": "Leseempfehlungen aus der Republik"
@@ -1432,6 +1436,10 @@
"key": "api/access/email/granter/claim_notice_gifted_membership/subject",
"value": "Geschenk-Mitgliedschaft von {recipientName} eingelöst"
},
+ {
+ "key": "api/access/email/granter/claim_notice_3months/subject",
+ "value": "Republik-Zugang wurde eingelöst"
+ },
{
"key": "api/access/grant/email/error",
"value": "Wir konnten «{email}» nicht als gültige E-Mail-Adresse erkennen."
@@ -1540,6 +1548,10 @@
"key": "api/access/resolvers/AccessGrant/status/unclaimed",
"value": "gültig, uneingelöst"
},
+ {
+ "key": "api/access/resolvers/AccessGrant/status/pending",
+ "value": "gültig, noch nicht begonnen"
+ },
{
"key": "api/access/resolvers/AccessGrant/status/valid",
"value": "gültig"
@@ -1554,7 +1566,11 @@
},
{
"key": "api/access/claim/can-not-claim-access-with-active-subscription",
- "value": "Ups, es ist ein Fehler aufgetreten. Kontaktieren Sie bitte das Support-Team via kontakt@republik.ch."
+ "value": "Sie haben bereits ein aktives Abonnement und können deshalb keinen Zugang einlösen."
+ },
+ {
+ "key": "api/access/claim/can-not-claim-access-with-active-membership",
+ "value": "Sie haben bereits eine aktive Mitgliedschaft oder ein aktives Abo und können deshalb keinen Zugang einlösen."
},
{
"key": "api/access/request/campaign/error",