@@ -10,6 +10,8 @@ import (
1010 "text/template"
1111 "time"
1212
13+ "github.com/raystack/frontier/core/authenticate"
14+
1315 "github.com/robfig/cron/v3"
1416
1517 "github.com/raystack/frontier/billing/credit"
@@ -35,6 +37,7 @@ const (
3537 AmountSubscriptionMetadataKey = "amount_total"
3638 CurrencySubscriptionMetadataKey = "currency"
3739 ProviderIDSubscriptionMetadataKey = "provider_subscription_id"
40+ InitiatorIDMetadataKey = "initiated_by"
3841)
3942
4043type Repository interface {
@@ -73,6 +76,10 @@ type OrganizationService interface {
7376 MemberCount (ctx context.Context , orgID string ) (int64 , error )
7477}
7578
79+ type AuthnService interface {
80+ GetPrincipal (ctx context.Context , assertions ... authenticate.ClientAssertion ) (authenticate.Principal , error )
81+ }
82+
7683type Service struct {
7784 stripeAutoTax bool
7885 stripeClient * client.API
@@ -83,6 +90,7 @@ type Service struct {
8390 creditService CreditService
8491 productService ProductService
8592 orgService OrganizationService
93+ authnService AuthnService
8694
8795 syncLimiter * debounce.Limiter
8896 syncJob * cron.Cron
@@ -92,7 +100,8 @@ type Service struct {
92100func NewService (stripeClient * client.API , stripeAutoTax bool , repository Repository ,
93101 customerService CustomerService , planService PlanService ,
94102 subscriptionService SubscriptionService , productService ProductService ,
95- creditService CreditService , orgService OrganizationService ) * Service {
103+ creditService CreditService , orgService OrganizationService ,
104+ authnService AuthnService ) * Service {
96105 s := & Service {
97106 stripeClient : stripeClient ,
98107 stripeAutoTax : stripeAutoTax ,
@@ -103,6 +112,7 @@ func NewService(stripeClient *client.API, stripeAutoTax bool, repository Reposit
103112 creditService : creditService ,
104113 productService : productService ,
105114 orgService : orgService ,
115+ authnService : authnService ,
106116 syncLimiter : debounce .New (2 * time .Second ),
107117 }
108118 return s
@@ -159,6 +169,11 @@ func (s *Service) Create(ctx context.Context, ch Checkout) (Checkout, error) {
159169 return Checkout {}, err
160170 }
161171
172+ currentPrincipal , err := s .authnService .GetPrincipal (ctx )
173+ if err != nil {
174+ return Checkout {}, err
175+ }
176+
162177 // checkout could be for a plan or a product
163178 if ch .PlanID != "" {
164179 plan , err := s .planService .GetByID (ctx , ch .PlanID )
@@ -218,7 +233,7 @@ func (s *Service) Create(ctx context.Context, ch Checkout) (Checkout, error) {
218233
219234 var trialDays * int64 = nil
220235 // if trial is enabled and user has not trialed before, set trial days
221- userHasTrialedBefore , err := s .hasUserTrialedBefore (ctx , billingCustomer .ID , plan .ID )
236+ userHasTrialedBefore , err := s .hasUserSubscribedBefore (ctx , billingCustomer .ID , plan .ID )
222237 if err != nil {
223238 return Checkout {}, err
224239 }
@@ -238,18 +253,20 @@ func (s *Service) Create(ctx context.Context, ch Checkout) (Checkout, error) {
238253 Customer : stripe .String (billingCustomer .ProviderID ),
239254 LineItems : subsItems ,
240255 Metadata : map [string ]string {
241- "org_id" : billingCustomer .OrgID ,
242- "plan_id" : ch .PlanID ,
243- "checkout_id" : checkoutID ,
244- "managed_by" : "frontier" ,
256+ "org_id" : billingCustomer .OrgID ,
257+ "plan_id" : ch .PlanID ,
258+ "checkout_id" : checkoutID ,
259+ InitiatorIDMetadataKey : currentPrincipal .ID ,
260+ "managed_by" : "frontier" ,
245261 },
246262 Mode : stripe .String (string (stripe .CheckoutSessionModeSubscription )),
247263 SubscriptionData : & stripe.CheckoutSessionSubscriptionDataParams {
248264 Description : stripe .String (fmt .Sprintf ("Checkout for %s" , plan .Name )),
249265 Metadata : map [string ]string {
250- "org_id" : billingCustomer .OrgID ,
251- "checkout_id" : checkoutID ,
252- "managed_by" : "frontier" ,
266+ "org_id" : billingCustomer .OrgID ,
267+ "checkout_id" : checkoutID ,
268+ subscription .InitiatorIDMetadataKey : currentPrincipal .ID ,
269+ "managed_by" : "frontier" ,
253270 },
254271 TrialPeriodDays : trialDays ,
255272 TrialSettings : & stripe.CheckoutSessionSubscriptionDataTrialSettingsParams {
@@ -320,11 +337,12 @@ func (s *Service) Create(ctx context.Context, ch Checkout) (Checkout, error) {
320337 LineItems : subsItems ,
321338 Mode : stripe .String (string (stripe .CheckoutSessionModePayment )),
322339 Metadata : map [string ]string {
323- "org_id" : billingCustomer .OrgID ,
324- "product_name" : chProduct .Name ,
325- "credit_amount" : fmt .Sprintf ("%d" , chProduct .Config .CreditAmount ),
326- "checkout_id" : checkoutID ,
327- "managed_by" : "frontier" ,
340+ "org_id" : billingCustomer .OrgID ,
341+ "product_name" : chProduct .Name ,
342+ "credit_amount" : fmt .Sprintf ("%d" , chProduct .Config .CreditAmount ),
343+ "checkout_id" : checkoutID ,
344+ InitiatorIDMetadataKey : currentPrincipal .ID ,
345+ "managed_by" : "frontier" ,
328346 },
329347 CancelURL : stripe .String (ch .CancelUrl ),
330348 SuccessURL : stripe .String (ch .SuccessUrl ),
@@ -385,16 +403,25 @@ func (s *Service) templatizeUrls(ch Checkout, checkoutID string) (Checkout, erro
385403 return ch , nil
386404}
387405
388- func (s * Service ) hasUserTrialedBefore (ctx context.Context , customerID string , planID string ) (bool , error ) {
406+ func (s * Service ) hasUserSubscribedBefore (ctx context.Context , customerID string , planID string ) (bool , error ) {
389407 subs , err := s .subscriptionService .List (ctx , subscription.Filter {
390408 CustomerID : customerID ,
391- PlanID : planID ,
392409 })
393410 if err != nil {
394411 return false , err
395412 }
396413 for _ , sub := range subs {
397- if sub .TrialEndsAt .Unix () > 0 {
414+ isPlanUsedBefore := false
415+ if sub .PlanID == planID {
416+ isPlanUsedBefore = true
417+ }
418+ for _ , history := range sub .PlanHistory {
419+ if history .PlanID == planID {
420+ isPlanUsedBefore = true
421+ }
422+ }
423+
424+ if isPlanUsedBefore {
398425 return true , nil
399426 }
400427 }
@@ -489,12 +516,18 @@ func (s *Service) ensureCreditsForProduct(ctx context.Context, ch Checkout) erro
489516 description = fmt .Sprintf ("addition of %d credits for %s at %d[%s]" , chProduct .Config .CreditAmount , chProduct .Title , price , currency )
490517 }
491518 }
519+ initiatorID := ""
520+ if id , ok := ch .Metadata [InitiatorIDMetadataKey ].(string ); ok {
521+ initiatorID = id
522+ }
492523 if err := s .creditService .Add (ctx , credit.Credit {
493524 ID : ch .ID ,
494525 AccountID : ch .CustomerID ,
495526 Amount : chProduct .Config .CreditAmount ,
496527 Metadata : ch .Metadata ,
497528 Description : description ,
529+ Source : credit .SourceSystemBuyEvent ,
530+ UserID : initiatorID ,
498531 }); err != nil && ! errors .Is (err , credit .ErrAlreadyApplied ) {
499532 return err
500533 }
0 commit comments