Skip to content

Models

Models hold the bulk of the functionality included in the dj-stripe package. Each model is tied closely to its corresponding object in the stripe dashboard. Fields that are not implemented for each model have a short reason behind the decision in the docstring for each model.

Core Resources

Attributes

djstripe.models.core.FileUpload = File module-attribute

Classes

djstripe.models.core.BalanceTransaction

Bases: StripeModel

A single transaction that updates the Stripe balance.

Stripe documentation: https://stripe.com/docs/api?lang=python#balance_transaction_object

Source code in djstripe/models/core.py
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
class BalanceTransaction(StripeModel):
    """
    A single transaction that updates the Stripe balance.

    Stripe documentation: https://stripe.com/docs/api?lang=python#balance_transaction_object
    """

    stripe_class = stripe.BalanceTransaction

    amount = StripeQuantumCurrencyAmountField(
        help_text="Gross amount of the transaction, in cents."
    )
    available_on = StripeDateTimeField(
        help_text=(
            "The date the transaction's net funds "
            "will become available in the Stripe balance."
        )
    )
    currency = StripeCurrencyCodeField()
    exchange_rate = models.DecimalField(null=True, decimal_places=6, max_digits=8)
    fee = StripeQuantumCurrencyAmountField(
        help_text="Fee (in cents) paid for this transaction."
    )
    fee_details = JSONField()
    net = StripeQuantumCurrencyAmountField(
        help_text="Net amount of the transaction, in cents."
    )
    source = StripeIdField()
    reporting_category = StripeEnumField(
        enum=enums.BalanceTransactionReportingCategory,
        help_text=(
            "More information: https://stripe.com/docs/reports/reporting-categories"
        ),
    )
    status = StripeEnumField(enum=enums.BalanceTransactionStatus)
    type = StripeEnumField(enum=enums.BalanceTransactionType)

    def __str__(self):
        amount = get_friendly_currency_amount(self.amount / 100, self.currency)
        status = enums.BalanceTransactionStatus.humanize(self.status)
        return f"{amount} ({status})"

    def get_source_class(self):
        try:
            return apps.get_model("djstripe", self.type)
        except LookupError:
            raise

    def get_source_instance(self):
        return self.get_source_class().objects.get(id=self.source)

    def get_stripe_dashboard_url(self):
        return self.get_source_instance().get_stripe_dashboard_url()

Attributes

djstripe.models.core.BalanceTransaction.amount = StripeQuantumCurrencyAmountField(help_text='Gross amount of the transaction, in cents.') class-attribute instance-attribute
djstripe.models.core.BalanceTransaction.available_on = StripeDateTimeField(help_text="The date the transaction's net funds will become available in the Stripe balance.") class-attribute instance-attribute
djstripe.models.core.BalanceTransaction.currency = StripeCurrencyCodeField() class-attribute instance-attribute
djstripe.models.core.BalanceTransaction.exchange_rate = models.DecimalField(null=True, decimal_places=6, max_digits=8) class-attribute instance-attribute
djstripe.models.core.BalanceTransaction.fee = StripeQuantumCurrencyAmountField(help_text='Fee (in cents) paid for this transaction.') class-attribute instance-attribute
djstripe.models.core.BalanceTransaction.fee_details = JSONField() class-attribute instance-attribute
djstripe.models.core.BalanceTransaction.net = StripeQuantumCurrencyAmountField(help_text='Net amount of the transaction, in cents.') class-attribute instance-attribute
djstripe.models.core.BalanceTransaction.reporting_category = StripeEnumField(enum=enums.BalanceTransactionReportingCategory, help_text='More information: https://stripe.com/docs/reports/reporting-categories') class-attribute instance-attribute
djstripe.models.core.BalanceTransaction.source = StripeIdField() class-attribute instance-attribute
djstripe.models.core.BalanceTransaction.status = StripeEnumField(enum=enums.BalanceTransactionStatus) class-attribute instance-attribute
djstripe.models.core.BalanceTransaction.stripe_class = stripe.BalanceTransaction class-attribute instance-attribute
djstripe.models.core.BalanceTransaction.type = StripeEnumField(enum=enums.BalanceTransactionType) class-attribute instance-attribute

Functions

djstripe.models.core.BalanceTransaction.__str__()
Source code in djstripe/models/core.py
90
91
92
93
def __str__(self):
    amount = get_friendly_currency_amount(self.amount / 100, self.currency)
    status = enums.BalanceTransactionStatus.humanize(self.status)
    return f"{amount} ({status})"
djstripe.models.core.BalanceTransaction.get_source_class()
Source code in djstripe/models/core.py
95
96
97
98
99
def get_source_class(self):
    try:
        return apps.get_model("djstripe", self.type)
    except LookupError:
        raise
djstripe.models.core.BalanceTransaction.get_source_instance()
Source code in djstripe/models/core.py
101
102
def get_source_instance(self):
    return self.get_source_class().objects.get(id=self.source)
djstripe.models.core.BalanceTransaction.get_stripe_dashboard_url()
Source code in djstripe/models/core.py
104
105
def get_stripe_dashboard_url(self):
    return self.get_source_instance().get_stripe_dashboard_url()

djstripe.models.core.Charge

Bases: StripeModel

To charge a credit or a debit card, you create a charge object. You can retrieve and refund individual charges as well as list all charges. Charges are identified by a unique random ID.

Stripe documentation: https://stripe.com/docs/api?lang=python#charges

Source code in djstripe/models/core.py
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
class Charge(StripeModel):
    """
    To charge a credit or a debit card, you create a charge object. You can
    retrieve and refund individual charges as well as list all charges. Charges
    are identified by a unique random ID.

    Stripe documentation: https://stripe.com/docs/api?lang=python#charges
    """

    stripe_class = stripe.Charge
    expand_fields = ["balance_transaction"]
    stripe_dashboard_item_name = "payments"

    amount = StripeDecimalCurrencyAmountField(help_text="Amount charged (as decimal).")
    amount_captured = StripeDecimalCurrencyAmountField(
        null=True,
        help_text=(
            "Amount (as decimal) captured (can be less than the amount attribute "
            "on the charge if a partial capture was issued)."
        ),
    )
    amount_refunded = StripeDecimalCurrencyAmountField(
        help_text=(
            "Amount (as decimal) refunded (can be less than the amount attribute on "
            "the charge if a partial refund was issued)."
        )
    )
    application = models.CharField(
        max_length=255,
        blank=True,
        help_text="ID of the Connect application that created the charge.",
    )
    application_fee = StripeForeignKey(
        "ApplicationFee",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="fee_for_charge",
        help_text="The application fee (if any) for the charge.",
    )
    application_fee_amount = StripeDecimalCurrencyAmountField(
        null=True,
        blank=True,
        help_text=(
            "The amount (as decimal) of the application fee (if any) "
            "requested for the charge."
        ),
    )
    balance_transaction = StripeForeignKey(
        "BalanceTransaction",
        on_delete=models.SET_NULL,
        null=True,
        help_text=(
            "The balance transaction that describes the impact of this charge "
            "on your account balance (not including refunds or disputes)."
        ),
    )
    billing_details = JSONField(
        null=True,
        help_text=(
            "Billing information associated with the PaymentMethod at the "
            "time of the transaction."
        ),
    )
    calculated_statement_descriptor = models.CharField(
        max_length=22,
        default="",
        help_text=(
            "The full statement descriptor that is passed to card networks, "
            "and that is displayed on your customers' credit card and bank statements. "
            "Allows you to see what the statement descriptor looks like after the "
            "static and dynamic portions are combined."
        ),
    )
    captured = models.BooleanField(
        default=False,
        help_text=(
            "If the charge was created without capturing, this boolean represents"
            " whether or not it is still uncaptured or has since been captured."
        ),
    )
    currency = StripeCurrencyCodeField(
        help_text="The currency in which the charge was made."
    )
    customer = StripeForeignKey(
        "Customer",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="charges",
        help_text="The customer associated with this charge.",
    )

    dispute = StripeForeignKey(
        "Dispute",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="charges",
        help_text="Details about the dispute if the charge has been disputed.",
    )
    disputed = models.BooleanField(
        default=False,
        help_text="Whether the charge has been disputed.",
    )
    failure_code = StripeEnumField(
        enum=enums.ApiErrorCode,
        default="",
        blank=True,
        help_text="Error code explaining reason for charge failure if available.",
    )
    failure_message = models.TextField(
        max_length=5000,
        default="",
        blank=True,
        help_text=(
            "Message to user further explaining reason for charge failure if available."
        ),
    )
    fraud_details = JSONField(
        help_text="Hash with information on fraud assessments for the charge.",
        null=True,
        blank=True,
    )
    invoice = StripeForeignKey(
        "Invoice",
        on_delete=models.CASCADE,
        null=True,
        related_name="charges",
        help_text="The invoice this charge is for if one exists.",
    )
    # TODO: order (requires Order model)
    on_behalf_of = StripeForeignKey(
        "Account",
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name="charges",
        help_text=(
            "The account (if any) the charge was made on behalf of "
            "without triggering an automatic transfer."
        ),
    )
    outcome = JSONField(
        help_text="Details about whether or not the payment was accepted, and why.",
        null=True,
        blank=True,
    )
    paid = models.BooleanField(
        default=False,
        help_text=(
            "True if the charge succeeded, "
            "or was successfully authorized for later capture, False otherwise."
        ),
    )
    payment_intent = StripeForeignKey(
        "PaymentIntent",
        null=True,
        on_delete=models.SET_NULL,
        related_name="charges",
        help_text="PaymentIntent associated with this charge, if one exists.",
    )
    payment_method = StripeForeignKey(
        "PaymentMethod",
        null=True,
        on_delete=models.SET_NULL,
        related_name="charges",
        help_text="PaymentMethod used in this charge.",
    )
    payment_method_details = JSONField(
        help_text="Details about the payment method at the time of the transaction.",
        null=True,
        blank=True,
    )
    receipt_email = models.TextField(
        max_length=800,  # yup, 800.
        default="",
        blank=True,
        help_text="The email address that the receipt for this charge was sent to.",
    )
    receipt_number = models.CharField(
        max_length=14,
        default="",
        blank=True,
        help_text=(
            "The transaction number that appears "
            "on email receipts sent for this charge."
        ),
    )
    receipt_url = models.TextField(
        max_length=5000,
        default="",
        blank=True,
        help_text=(
            "This is the URL to view the receipt for this charge. "
            "The receipt is kept up-to-date to the latest state of the charge, "
            "including any refunds. If the charge is for an Invoice, "
            "the receipt will be stylized as an Invoice receipt."
        ),
    )
    refunded = models.BooleanField(
        default=False,
        help_text=(
            "Whether or not the charge has been fully refunded. "
            "If the charge is only partially refunded, "
            "this attribute will still be false."
        ),
    )
    # TODO: review (requires Review model)
    shipping = JSONField(
        null=True, blank=True, help_text="Shipping information for the charge"
    )
    source = PaymentMethodForeignKey(
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="charges",
        help_text="The source used for this charge.",
    )
    source_transfer = StripeForeignKey(
        "Transfer",
        null=True,
        blank=True,
        on_delete=models.CASCADE,
        help_text=(
            "The transfer which created this charge. Only present if the "
            "charge came from another Stripe account."
        ),
        related_name="+",
    )
    statement_descriptor = models.CharField(
        max_length=22,
        null=True,
        blank=True,
        help_text=(
            "For card charges, use statement_descriptor_suffix instead. "
            "Otherwise, you can use this value as the complete description of a "
            "charge on your customers' statements. Must contain at least one letter, "
            "maximum 22 characters."
        ),
    )
    statement_descriptor_suffix = models.CharField(
        max_length=22,
        null=True,
        blank=True,
        help_text=(
            "Provides information about the charge that customers see on "
            "their statements. Concatenated with the prefix (shortened descriptor) "
            "or statement descriptor that's set on the account to form the "
            "complete statement descriptor. "
            "Maximum 22 characters for the concatenated descriptor."
        ),
    )
    status = StripeEnumField(
        enum=enums.ChargeStatus, help_text="The status of the payment."
    )
    transfer = StripeForeignKey(
        "Transfer",
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        help_text=(
            "The transfer to the `destination` account (only applicable if "
            "the charge was created using the `destination` parameter)."
        ),
    )
    transfer_data = JSONField(
        null=True,
        blank=True,
        help_text=(
            "An optional dictionary including the account to automatically "
            "transfer to as part of a destination charge."
        ),
    )
    transfer_group = models.CharField(
        max_length=255,
        null=True,
        blank=True,
        help_text="A string that identifies this transaction as part of a group.",
    )

    objects = ChargeManager()

    def __str__(self):
        amount = get_friendly_currency_amount(self.amount, self.currency)
        return f"{amount} ({self.human_readable_status})"

    @property
    def fee(self):
        if self.balance_transaction:
            return self.balance_transaction.fee

    @property
    def human_readable_status(self) -> str:
        if not self.captured:
            return "Uncaptured"
        elif self.disputed:
            return "Disputed"
        elif self.refunded:
            return "Refunded"
        return enums.ChargeStatus.humanize(self.status)

    @property
    def fraudulent(self) -> bool:
        return (
            self.fraud_details and list(self.fraud_details.values())[0] == "fraudulent"
        )

    def _calculate_refund_amount(self, amount: Optional[Decimal]) -> int:
        """
        Returns the amount that can be refunded (in cents)
        """
        eligible_to_refund = self.amount - (self.amount_refunded or 0)
        amount_to_refund = (
            min(eligible_to_refund, amount) if amount else eligible_to_refund
        )

        return int(amount_to_refund * 100)

    def refund(
        self,
        amount: Decimal = None,
        reason: str = None,
        api_key: str = None,
        stripe_account: str = None,
    ) -> "Refund":
        """
        Initiate a refund. Returns the refund object.

        :param amount: A positive decimal amount representing how much of this charge
            to refund. If amount is not provided, then this will be a full refund.
            Can only refund up to the unrefunded amount remaining of the charge.
        :param reason: String indicating the reason for the refund.
            If set, possible values are ``duplicate``, ``fraudulent``,
            and ``requested_by_customer``. Specifying ``fraudulent`` as the reason
            when you believe the charge to be fraudulent will
            help Stripe improve their fraud detection algorithms.
        """
        api_key = api_key or self.default_api_key

        # Prefer passed in stripe_account if set.
        if not stripe_account:
            stripe_account = self._get_stripe_account_id(api_key)

        refund_obj = Refund._api_create(
            charge=self.id,
            amount=self._calculate_refund_amount(amount=amount),
            reason=reason,
            api_key=api_key,
            stripe_account=stripe_account,
        )

        return Refund.sync_from_stripe_data(
            refund_obj,
            api_key=api_key,
            stripe_version=djstripe_settings.STRIPE_API_VERSION,
        )

    def capture(self, **kwargs) -> "Charge":
        """
        Capture the payment of an existing, uncaptured, charge.
        This is the second half of the two-step payment flow, where first you
        created a charge with the capture option set to False.

        See https://stripe.com/docs/api#capture_charge
        """

        captured_charge = self.api_retrieve().capture(**kwargs)
        return self.__class__.sync_from_stripe_data(
            captured_charge, api_key=self.default_api_key
        )

    def _attach_objects_post_save_hook(
        self,
        cls,
        data,
        pending_relations=None,
        api_key=djstripe_settings.STRIPE_SECRET_KEY,
    ):
        super()._attach_objects_post_save_hook(
            cls, data, pending_relations=pending_relations, api_key=api_key
        )

        cls._stripe_object_to_refunds(
            target_cls=Refund, data=data, charge=self, api_key=api_key
        )

Attributes

djstripe.models.core.Charge.amount = StripeDecimalCurrencyAmountField(help_text='Amount charged (as decimal).') class-attribute instance-attribute
djstripe.models.core.Charge.amount_captured = StripeDecimalCurrencyAmountField(null=True, help_text='Amount (as decimal) captured (can be less than the amount attribute on the charge if a partial capture was issued).') class-attribute instance-attribute
djstripe.models.core.Charge.amount_refunded = StripeDecimalCurrencyAmountField(help_text='Amount (as decimal) refunded (can be less than the amount attribute on the charge if a partial refund was issued).') class-attribute instance-attribute
djstripe.models.core.Charge.application = models.CharField(max_length=255, blank=True, help_text='ID of the Connect application that created the charge.') class-attribute instance-attribute
djstripe.models.core.Charge.application_fee = StripeForeignKey('ApplicationFee', on_delete=models.SET_NULL, null=True, blank=True, related_name='fee_for_charge', help_text='The application fee (if any) for the charge.') class-attribute instance-attribute
djstripe.models.core.Charge.application_fee_amount = StripeDecimalCurrencyAmountField(null=True, blank=True, help_text='The amount (as decimal) of the application fee (if any) requested for the charge.') class-attribute instance-attribute
djstripe.models.core.Charge.balance_transaction = StripeForeignKey('BalanceTransaction', on_delete=models.SET_NULL, null=True, help_text='The balance transaction that describes the impact of this charge on your account balance (not including refunds or disputes).') class-attribute instance-attribute
djstripe.models.core.Charge.billing_details = JSONField(null=True, help_text='Billing information associated with the PaymentMethod at the time of the transaction.') class-attribute instance-attribute
djstripe.models.core.Charge.calculated_statement_descriptor = models.CharField(max_length=22, default='', help_text="The full statement descriptor that is passed to card networks, and that is displayed on your customers' credit card and bank statements. Allows you to see what the statement descriptor looks like after the static and dynamic portions are combined.") class-attribute instance-attribute
djstripe.models.core.Charge.captured = models.BooleanField(default=False, help_text='If the charge was created without capturing, this boolean represents whether or not it is still uncaptured or has since been captured.') class-attribute instance-attribute
djstripe.models.core.Charge.currency = StripeCurrencyCodeField(help_text='The currency in which the charge was made.') class-attribute instance-attribute
djstripe.models.core.Charge.customer = StripeForeignKey('Customer', on_delete=models.SET_NULL, null=True, blank=True, related_name='charges', help_text='The customer associated with this charge.') class-attribute instance-attribute
djstripe.models.core.Charge.dispute = StripeForeignKey('Dispute', on_delete=models.SET_NULL, null=True, blank=True, related_name='charges', help_text='Details about the dispute if the charge has been disputed.') class-attribute instance-attribute
djstripe.models.core.Charge.disputed = models.BooleanField(default=False, help_text='Whether the charge has been disputed.') class-attribute instance-attribute
djstripe.models.core.Charge.expand_fields = ['balance_transaction'] class-attribute instance-attribute
djstripe.models.core.Charge.failure_code = StripeEnumField(enum=enums.ApiErrorCode, default='', blank=True, help_text='Error code explaining reason for charge failure if available.') class-attribute instance-attribute
djstripe.models.core.Charge.failure_message = models.TextField(max_length=5000, default='', blank=True, help_text='Message to user further explaining reason for charge failure if available.') class-attribute instance-attribute
djstripe.models.core.Charge.fee property
djstripe.models.core.Charge.fraud_details = JSONField(help_text='Hash with information on fraud assessments for the charge.', null=True, blank=True) class-attribute instance-attribute
djstripe.models.core.Charge.fraudulent: bool property
djstripe.models.core.Charge.human_readable_status: str property
djstripe.models.core.Charge.invoice = StripeForeignKey('Invoice', on_delete=models.CASCADE, null=True, related_name='charges', help_text='The invoice this charge is for if one exists.') class-attribute instance-attribute
djstripe.models.core.Charge.objects = ChargeManager() class-attribute instance-attribute
djstripe.models.core.Charge.on_behalf_of = StripeForeignKey('Account', on_delete=models.CASCADE, null=True, blank=True, related_name='charges', help_text='The account (if any) the charge was made on behalf of without triggering an automatic transfer.') class-attribute instance-attribute
djstripe.models.core.Charge.outcome = JSONField(help_text='Details about whether or not the payment was accepted, and why.', null=True, blank=True) class-attribute instance-attribute
djstripe.models.core.Charge.paid = models.BooleanField(default=False, help_text='True if the charge succeeded, or was successfully authorized for later capture, False otherwise.') class-attribute instance-attribute
djstripe.models.core.Charge.payment_intent = StripeForeignKey('PaymentIntent', null=True, on_delete=models.SET_NULL, related_name='charges', help_text='PaymentIntent associated with this charge, if one exists.') class-attribute instance-attribute
djstripe.models.core.Charge.payment_method = StripeForeignKey('PaymentMethod', null=True, on_delete=models.SET_NULL, related_name='charges', help_text='PaymentMethod used in this charge.') class-attribute instance-attribute
djstripe.models.core.Charge.payment_method_details = JSONField(help_text='Details about the payment method at the time of the transaction.', null=True, blank=True) class-attribute instance-attribute
djstripe.models.core.Charge.receipt_email = models.TextField(max_length=800, default='', blank=True, help_text='The email address that the receipt for this charge was sent to.') class-attribute instance-attribute
djstripe.models.core.Charge.receipt_number = models.CharField(max_length=14, default='', blank=True, help_text='The transaction number that appears on email receipts sent for this charge.') class-attribute instance-attribute
djstripe.models.core.Charge.receipt_url = models.TextField(max_length=5000, default='', blank=True, help_text='This is the URL to view the receipt for this charge. The receipt is kept up-to-date to the latest state of the charge, including any refunds. If the charge is for an Invoice, the receipt will be stylized as an Invoice receipt.') class-attribute instance-attribute
djstripe.models.core.Charge.refunded = models.BooleanField(default=False, help_text='Whether or not the charge has been fully refunded. If the charge is only partially refunded, this attribute will still be false.') class-attribute instance-attribute
djstripe.models.core.Charge.shipping = JSONField(null=True, blank=True, help_text='Shipping information for the charge') class-attribute instance-attribute
djstripe.models.core.Charge.source = PaymentMethodForeignKey(on_delete=models.SET_NULL, null=True, blank=True, related_name='charges', help_text='The source used for this charge.') class-attribute instance-attribute
djstripe.models.core.Charge.source_transfer = StripeForeignKey('Transfer', null=True, blank=True, on_delete=models.CASCADE, help_text='The transfer which created this charge. Only present if the charge came from another Stripe account.', related_name='+') class-attribute instance-attribute
djstripe.models.core.Charge.statement_descriptor = models.CharField(max_length=22, null=True, blank=True, help_text="For card charges, use statement_descriptor_suffix instead. Otherwise, you can use this value as the complete description of a charge on your customers' statements. Must contain at least one letter, maximum 22 characters.") class-attribute instance-attribute
djstripe.models.core.Charge.statement_descriptor_suffix = models.CharField(max_length=22, null=True, blank=True, help_text="Provides information about the charge that customers see on their statements. Concatenated with the prefix (shortened descriptor) or statement descriptor that's set on the account to form the complete statement descriptor. Maximum 22 characters for the concatenated descriptor.") class-attribute instance-attribute
djstripe.models.core.Charge.status = StripeEnumField(enum=enums.ChargeStatus, help_text='The status of the payment.') class-attribute instance-attribute
djstripe.models.core.Charge.stripe_class = stripe.Charge class-attribute instance-attribute
djstripe.models.core.Charge.stripe_dashboard_item_name = 'payments' class-attribute instance-attribute
djstripe.models.core.Charge.transfer = StripeForeignKey('Transfer', on_delete=models.CASCADE, null=True, blank=True, help_text='The transfer to the `destination` account (only applicable if the charge was created using the `destination` parameter).') class-attribute instance-attribute
djstripe.models.core.Charge.transfer_data = JSONField(null=True, blank=True, help_text='An optional dictionary including the account to automatically transfer to as part of a destination charge.') class-attribute instance-attribute
djstripe.models.core.Charge.transfer_group = models.CharField(max_length=255, null=True, blank=True, help_text='A string that identifies this transaction as part of a group.') class-attribute instance-attribute

Functions

djstripe.models.core.Charge.__str__()
Source code in djstripe/models/core.py
391
392
393
def __str__(self):
    amount = get_friendly_currency_amount(self.amount, self.currency)
    return f"{amount} ({self.human_readable_status})"
djstripe.models.core.Charge.capture(**kwargs)

Capture the payment of an existing, uncaptured, charge. This is the second half of the two-step payment flow, where first you created a charge with the capture option set to False.

See https://stripe.com/docs/api#capture_charge

Source code in djstripe/models/core.py
466
467
468
469
470
471
472
473
474
475
476
477
478
def capture(self, **kwargs) -> "Charge":
    """
    Capture the payment of an existing, uncaptured, charge.
    This is the second half of the two-step payment flow, where first you
    created a charge with the capture option set to False.

    See https://stripe.com/docs/api#capture_charge
    """

    captured_charge = self.api_retrieve().capture(**kwargs)
    return self.__class__.sync_from_stripe_data(
        captured_charge, api_key=self.default_api_key
    )
djstripe.models.core.Charge.refund(amount=None, reason=None, api_key=None, stripe_account=None)

Initiate a refund. Returns the refund object.

:param amount: A positive decimal amount representing how much of this charge to refund. If amount is not provided, then this will be a full refund. Can only refund up to the unrefunded amount remaining of the charge. :param reason: String indicating the reason for the refund. If set, possible values are duplicate, fraudulent, and requested_by_customer. Specifying fraudulent as the reason when you believe the charge to be fraudulent will help Stripe improve their fraud detection algorithms.

Source code in djstripe/models/core.py
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
def refund(
    self,
    amount: Decimal = None,
    reason: str = None,
    api_key: str = None,
    stripe_account: str = None,
) -> "Refund":
    """
    Initiate a refund. Returns the refund object.

    :param amount: A positive decimal amount representing how much of this charge
        to refund. If amount is not provided, then this will be a full refund.
        Can only refund up to the unrefunded amount remaining of the charge.
    :param reason: String indicating the reason for the refund.
        If set, possible values are ``duplicate``, ``fraudulent``,
        and ``requested_by_customer``. Specifying ``fraudulent`` as the reason
        when you believe the charge to be fraudulent will
        help Stripe improve their fraud detection algorithms.
    """
    api_key = api_key or self.default_api_key

    # Prefer passed in stripe_account if set.
    if not stripe_account:
        stripe_account = self._get_stripe_account_id(api_key)

    refund_obj = Refund._api_create(
        charge=self.id,
        amount=self._calculate_refund_amount(amount=amount),
        reason=reason,
        api_key=api_key,
        stripe_account=stripe_account,
    )

    return Refund.sync_from_stripe_data(
        refund_obj,
        api_key=api_key,
        stripe_version=djstripe_settings.STRIPE_API_VERSION,
    )

djstripe.models.core.Customer

Bases: StripeModel

Customer objects allow you to perform recurring charges and track multiple charges that are associated with the same customer.

Stripe documentation: https://stripe.com/docs/api?lang=python#customers

Source code in djstripe/models/core.py
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
class Customer(StripeModel):
    """
    Customer objects allow you to perform recurring charges and track multiple
    charges that are associated with the same customer.

    Stripe documentation: https://stripe.com/docs/api?lang=python#customers
    """

    stripe_class = stripe.Customer
    expand_fields = ["default_source", "sources"]
    stripe_dashboard_item_name = "customers"

    address = JSONField(null=True, blank=True, help_text="The customer's address.")
    balance = StripeQuantumCurrencyAmountField(
        null=True,
        blank=True,
        default=0,
        help_text=(
            "Current balance (in cents), if any, being stored on the customer's "
            "account. "
            "If negative, the customer has credit to apply to the next invoice. "
            "If positive, the customer has an amount owed that will be added to the "
            "next invoice. The balance does not refer to any unpaid invoices; it "
            "solely takes into account amounts that have yet to be successfully "
            "applied to any invoice. This balance is only taken into account for "
            "recurring billing purposes (i.e., subscriptions, invoices, invoice items)."
        ),
    )
    currency = StripeCurrencyCodeField(
        blank=True,
        default="",
        help_text=(
            "The currency the customer can be charged in for recurring billing purposes"
        ),
    )
    default_source = PaymentMethodForeignKey(
        on_delete=models.SET_NULL, null=True, blank=True, related_name="customers"
    )
    delinquent = models.BooleanField(
        null=True,
        blank=True,
        default=False,
        help_text=(
            "Whether or not the latest charge for the customer's "
            "latest invoice has failed."
        ),
    )
    # Stripe API returns deleted customers like so:
    # {
    #   "id": "cus_KX439W5dKrpi22",
    #   "object": "customer",
    #   "deleted": true,
    # }
    deleted = models.BooleanField(
        default=False,
        null=True,
        blank=True,
        help_text=(
            "Whether the Customer instance has been deleted upstream in Stripe or not."
        ),
    )
    # <discount>
    coupon = models.ForeignKey(
        "Coupon", null=True, blank=True, on_delete=models.SET_NULL
    )
    coupon_start = StripeDateTimeField(
        null=True,
        blank=True,
        editable=False,
        help_text="If a coupon is present, the date at which it was applied.",
    )
    coupon_end = StripeDateTimeField(
        null=True,
        blank=True,
        editable=False,
        help_text=(
            "If a coupon is present and has a limited duration, "
            "the date that the discount will end."
        ),
    )
    # </discount>
    discount = JSONField(
        null=True,
        blank=True,
        help_text=(
            "Describes the current discount active on the customer, if there is one."
        ),
    )

    email = models.TextField(max_length=5000, default="", blank=True)
    invoice_prefix = models.CharField(
        default="",
        blank=True,
        max_length=255,
        help_text=(
            "The prefix for the customer used to generate unique invoice numbers."
        ),
    )
    invoice_settings = JSONField(
        null=True, blank=True, help_text="The customer's default invoice settings."
    )
    # default_payment_method is actually nested inside invoice_settings
    # this field is a convenience to provide the foreign key
    default_payment_method = StripeForeignKey(
        "PaymentMethod",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+",
        help_text=(
            "default payment method used for subscriptions and invoices "
            "for the customer."
        ),
    )
    name = models.TextField(
        max_length=5000,
        default="",
        blank=True,
        help_text="The customer's full name or business name.",
    )
    phone = models.TextField(
        max_length=5000,
        default="",
        blank=True,
        help_text="The customer's phone number.",
    )
    preferred_locales = JSONField(
        null=True,
        blank=True,
        help_text=(
            "The customer's preferred locales (languages), ordered by preference."
        ),
    )
    shipping = JSONField(
        null=True,
        blank=True,
        help_text="Shipping information associated with the customer.",
    )
    tax_exempt = StripeEnumField(
        enum=enums.CustomerTaxExempt,
        default="",
        help_text=(
            "Describes the customer's tax exemption status. When set to reverse, "
            'invoice and receipt PDFs include the text "Reverse charge".'
        ),
    )

    # dj-stripe fields
    subscriber = models.ForeignKey(
        djstripe_settings.get_subscriber_model_string(),
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
        related_name="djstripe_customers",
    )
    date_purged = models.DateTimeField(null=True, editable=False)

    class Meta(StripeModel.Meta):
        unique_together = ("subscriber", "livemode", "djstripe_owner_account")

    def __str__(self):
        if self.subscriber:
            return str(self.subscriber)

        return self.name or self.description or self.id

    @classmethod
    def _manipulate_stripe_object_hook(cls, data):
        # stripe adds a deleted attribute if the Customer has been deleted upstream
        if data.get("deleted"):
            logger.warning(
                f"This customer ({data.get('id')}) has been deleted upstream, in Stripe"
            )

        else:
            # set "deleted" key to False (default)
            data["deleted"] = False

        discount = data.get("discount")
        if discount:
            data["coupon_start"] = discount["start"]
            data["coupon_end"] = discount["end"]

        # Populate the object id for our default_payment_method field (or set it None)
        data["default_payment_method"] = data.get("invoice_settings", {}).get(
            "default_payment_method"
        )

        return data

    @classmethod
    def get_or_create(
        cls,
        subscriber,
        livemode=djstripe_settings.STRIPE_LIVE_MODE,
        stripe_account=None,
    ):
        """
        Get or create a dj-stripe customer.

        :param subscriber: The subscriber model instance for which to get or
            create a customer.
        :type subscriber: User

        :param livemode: Whether to get the subscriber in live or test mode.
        :type livemode: bool
        """

        try:
            return cls.objects.get(subscriber=subscriber, livemode=livemode), False
        except cls.DoesNotExist:
            action = f"create:{subscriber.pk}"
            idempotency_key = djstripe_settings.get_idempotency_key(
                "customer", action, livemode
            )
            return (
                cls.create(
                    subscriber,
                    idempotency_key=idempotency_key,
                    stripe_account=stripe_account,
                ),
                True,
            )

    @classmethod
    def create(cls, subscriber, idempotency_key=None, stripe_account=None):
        metadata = {}
        subscriber_key = djstripe_settings.SUBSCRIBER_CUSTOMER_KEY
        if subscriber_key not in ("", None):
            metadata[subscriber_key] = subscriber.pk

        try:
            # if subscriber table has a get_full_name() method, use it as name
            # ref django.contrib.auth.models.User.get_full_name
            name = subscriber.get_full_name()
        except AttributeError:
            name = None

        stripe_customer = cls._api_create(
            email=subscriber.email,
            name=name,
            idempotency_key=idempotency_key,
            metadata=metadata,
            stripe_account=stripe_account,
        )
        customer, created = cls.objects.get_or_create(
            id=stripe_customer["id"],
            defaults={
                "subscriber": subscriber,
                "livemode": stripe_customer["livemode"],
                "balance": stripe_customer.get("balance", 0),
                "delinquent": stripe_customer.get("delinquent", False),
            },
        )

        return customer

    @property
    def credits(self):
        """
        The customer is considered to have credits if their balance is below 0.
        """
        return abs(min(self.balance, 0))

    @property
    def customer_payment_methods(self):
        """
        An iterable of all of the customer's payment methods
        (sources, then legacy cards)
        """
        for source in self.sources.iterator():
            yield source

        for card in self.legacy_cards.iterator():
            yield card

    @property
    def pending_charges(self):
        """
        The customer is considered to have pending charges if their balance is above 0.
        """
        return max(self.balance, 0)

    def subscribe(self, *, items=None, price=None, plan=None, **kwargs):
        """
        Subscribes this customer to all the prices or plans in the items dict (Recommended).

        :param items: A list of up to 20 subscription items, each with an attached price
        :type list:
            :param items: A dictionary of Plan (or Plan ID) or Price (or Price ID)
            :type dict:  The price or plan to which to subscribe the customer.

        :param price: The price to which to subscribe the customer.
        :type price: Price or string (price ID)

        :param plan: The plan to which to subscribe the customer.
        :type plan: Plan or string (plan ID)
        """
        from .billing import Subscription

        if (items and price) or (items and plan) or (price and plan):
            raise TypeError("Please define only one of items, price or plan arguments.")

        if items is None:
            _items = [{"price": price}]
        else:
            _items = []
            for item in items:
                price = item.get("price", "")
                plan = item.get("plan", "")
                price, kwargs = _sanitise_price(price, plan, **kwargs)
                if "price" in item:
                    _items.append({"price": price})
                if "plan" in item:
                    _items.append({"plan": price})

        stripe_subscription = Subscription._api_create(
            items=_items, customer=self.id, **kwargs
        )

        api_key = kwargs.get("api_key") or self.default_api_key
        return Subscription.sync_from_stripe_data(stripe_subscription, api_key=api_key)

    def charge(
        self,
        amount: Decimal,
        *,
        application_fee: Decimal = None,
        source: Union[str, StripeModel] = None,
        **kwargs,
    ) -> Charge:
        """
        Creates a charge for this customer.

        :param amount: The amount to charge.
        :type amount: Decimal. Precision is 2; anything more will be ignored.
        :param source: The source to use for this charge.
            Must be a source attributed to this customer. If None, the customer's
            default source is used. Can be either the id of the source or
            the source object itself.
        :type source: string, Source
        """

        if not isinstance(amount, Decimal):
            raise ValueError("You must supply a decimal value representing dollars.")

        # Convert Source to id
        if source and isinstance(source, StripeModel):
            source = source.id

        stripe_charge = Charge._api_create(
            customer=self.id,
            amount=int(amount * 100),  # Convert dollars into cents
            application_fee=(
                int(application_fee * 100) if application_fee else None
            ),  # Convert dollars into cents
            source=source,
            **kwargs,
        )

        api_key = kwargs.get("api_key") or self.default_api_key
        return Charge.sync_from_stripe_data(stripe_charge, api_key=api_key)

    def add_invoice_item(
        self,
        amount,
        currency,
        description=None,
        discountable=None,
        invoice=None,
        metadata=None,
        subscription=None,
    ):
        """
        Adds an arbitrary charge or credit to the customer's upcoming invoice.
        Different than creating a charge. Charges are separate bills that get
        processed immediately. Invoice items are appended to the customer's next
        invoice. This is extremely useful when adding surcharges to subscriptions.

        :param amount: The amount to charge.
        :type amount: Decimal. Precision is 2; anything more will be ignored.
        :param currency: 3-letter ISO code for currency
        :type currency: string
        :param description: An arbitrary string.
        :type description: string
        :param discountable: Controls whether discounts apply to this invoice item.
            Defaults to False for prorations or negative invoice items,
            and True for all other invoice items.
        :type discountable: boolean
        :param invoice: An existing invoice to add this invoice item to.
            When left blank, the invoice item will be added to the next upcoming \
             scheduled invoice. \
             Use this when adding invoice items in response to an \
             ``invoice.created`` webhook. You cannot add an invoice \
            item to an invoice that has already been paid, attempted or closed.
        :type invoice: Invoice or string (invoice ID)
        :param metadata: A set of key/value pairs useful for storing
            additional information.
        :type metadata: dict
        :param subscription: A subscription to add this invoice item to.
            When left blank, the invoice item will be be added to the next upcoming \
            scheduled invoice. When set, scheduled invoices for subscriptions other \
            than the specified subscription will ignore the invoice item. \
            Use this when you want to express that an invoice item has been accrued \
            within the context of a particular subscription.
        :type subscription: Subscription or string (subscription ID)

        .. Notes:
        .. if you're using ``Customer.add_invoice_item()`` instead of
        .. ``Customer.add_invoice_item()``, ``invoice`` and ``subscriptions``
        .. can only be strings
        """
        from .billing import InvoiceItem

        if not isinstance(amount, Decimal):
            raise ValueError("You must supply a decimal value representing dollars.")

        # Convert Invoice to id
        if invoice is not None and isinstance(invoice, StripeModel):
            invoice = invoice.id

        # Convert Subscription to id
        if subscription is not None and isinstance(subscription, StripeModel):
            subscription = subscription.id

        stripe_invoiceitem = InvoiceItem._api_create(
            amount=int(amount * 100),  # Convert dollars into cents
            currency=currency,
            customer=self.id,
            description=description,
            discountable=discountable,
            invoice=invoice,
            metadata=metadata,
            subscription=subscription,
        )

        return InvoiceItem.sync_from_stripe_data(
            stripe_invoiceitem, api_key=self.default_api_key
        )

    def add_card(self, source, set_default=True):
        """
        Adds a card to this customer's account.

        :param source: Either a token, like the ones returned by our Stripe.js, or a
            dictionary containing a user's credit card details.
            Stripe will automatically validate the card.
        :type source: string, dict
        :param set_default: Whether or not to set the source as the customer's
            default source
        :type set_default: boolean

        """
        from .payment_methods import DjstripePaymentMethod

        stripe_customer = self.api_retrieve()
        new_stripe_payment_method = stripe_customer.sources.create(source=source)

        if set_default:
            stripe_customer.default_source = new_stripe_payment_method["id"]
            stripe_customer.save()

        new_payment_method = DjstripePaymentMethod.from_stripe_object(
            new_stripe_payment_method
        )

        # Change the default source
        if set_default:
            self.default_source = new_payment_method
            self.save()

        return new_payment_method.resolve()

    def add_payment_method(self, payment_method, set_default=True):
        """
        Adds an already existing payment method to this customer's account

        :param payment_method: PaymentMethod to be attached to the customer
        :type payment_method: str, PaymentMethod
        :param set_default: If true, this will be set as the default_payment_method
        :type set_default: bool
        :rtype: PaymentMethod
        """
        from .payment_methods import PaymentMethod

        stripe_customer = self.api_retrieve()
        payment_method = PaymentMethod.attach(payment_method, stripe_customer)

        if set_default:
            stripe_customer["invoice_settings"][
                "default_payment_method"
            ] = payment_method.id
            stripe_customer.save()

            # Refresh self from the stripe customer, this should have two effects:
            # 1) sets self.default_payment_method (we rely on logic in
            # Customer._manipulate_stripe_object_hook to do this)
            # 2) updates self.invoice_settings.default_payment_methods
            self.sync_from_stripe_data(stripe_customer, api_key=self.default_api_key)
            self.refresh_from_db()

        return payment_method

    def purge(self):
        """Customers are soft deleted as deleted customers are still accessible by the
        Stripe API and sync for all RelatedModels would fail"""
        try:
            self._api_delete()
        except InvalidRequestError as exc:
            if "No such customer:" in str(exc):
                # The exception was thrown because the stripe customer was already
                # deleted on the stripe side, ignore the exception
                pass
            else:
                # The exception was raised for another reason, re-raise it
                raise

        # toggle the deleted flag on Customer to indicate it has been
        # deleted upstream in Stripe
        self.deleted = True

        if self.subscriber:
            # Delete the idempotency key used by Customer.create()
            # So re-creating a customer for this subscriber before the key expires
            # doesn't return the older Customer data
            idempotency_key_action = f"customer:create:{self.subscriber.pk}"
            IdempotencyKey.objects.filter(action=idempotency_key_action).delete()

        self.subscriber = None

        # Remove sources
        self.default_source = None
        for source in self.legacy_cards.all():
            source.remove()

        for source in self.sources.all():
            source.detach()

        self.date_purged = timezone.now()
        self.save()

    def _get_valid_subscriptions(self):
        """Get a list of this customer's valid subscriptions."""

        return [
            subscription
            for subscription in self.subscriptions.all()
            if subscription.is_valid()
        ]

    def is_subscribed_to(self, product: Union[Product, str]) -> bool:
        """
        Checks to see if this customer has an active subscription to the given product.

        :param product: The product for which to check for an active subscription.
        :type product: Product or string (product ID)

        :returns: True if there exists an active subscription, False otherwise.
        """

        if isinstance(product, StripeModel):
            product = product.id

        for subscription in self._get_valid_subscriptions():
            for item in subscription.items.all():
                if item.price and item.price.product.id == product:
                    return True
        return False

    def has_any_active_subscription(self):
        """
        Checks to see if this customer has an active subscription to any plan.

        :returns: True if there exists an active subscription, False otherwise.
        """

        return len(self._get_valid_subscriptions()) != 0

    @property
    def active_subscriptions(self):
        """
        Returns active subscriptions
        (subscriptions with an active status that end in the future).
        """
        return self.subscriptions.filter(
            status=enums.SubscriptionStatus.active,
            current_period_end__gt=timezone.now(),
        )

    @property
    def valid_subscriptions(self):
        """
        Returns this customer's valid subscriptions
        (subscriptions that aren't canceled or incomplete_expired).
        """
        return self.subscriptions.exclude(
            status__in=[
                enums.SubscriptionStatus.canceled,
                enums.SubscriptionStatus.incomplete_expired,
            ]
        )

    @property
    def subscription(self):
        """
        Shortcut to get this customer's subscription.

        :returns: None if the customer has no subscriptions, the subscription if
            the customer has a subscription.
        :raises MultipleSubscriptionException: Raised if the customer has multiple
            subscriptions.
            In this case, use ``Customer.subscriptions`` instead.
        """

        subscriptions = self.valid_subscriptions

        if subscriptions.count() > 1:
            raise MultipleSubscriptionException(
                "This customer has multiple subscriptions. Use Customer.subscriptions "
                "to access them."
            )
        else:
            return subscriptions.first()

    def send_invoice(self, **kwargs):
        """
        Pay and send the customer's latest invoice.

        :returns: True if an invoice was able to be created and paid, False otherwise
            (typically if there was nothing to invoice).
        """
        from .billing import Invoice

        try:
            invoice = Invoice._api_create(customer=self.id)
            invoice.pay(**kwargs)
            return True
        except InvalidRequestError:  # TODO: Check this for a more
            #                           specific error message.
            return False  # There was nothing to invoice

    def retry_unpaid_invoices(self, **kwargs):
        """Attempt to retry collecting payment on the customer's unpaid invoices."""

        self._sync_invoices()
        for invoice in self.invoices.filter(auto_advance=True).exclude(status="paid"):
            try:
                invoice.retry(**kwargs)  # Always retry unpaid invoices
            except InvalidRequestError as exc:
                if str(exc) != "Invoice is already paid":
                    raise

    def add_coupon(self, coupon, idempotency_key=None):
        """
        Add a coupon to a Customer.

        The coupon can be a Coupon object, or a valid Stripe Coupon ID.
        """
        if isinstance(coupon, StripeModel):
            coupon = coupon.id

        stripe_customer = self.api_retrieve()
        stripe_customer["coupon"] = coupon
        stripe_customer.save(idempotency_key=idempotency_key)
        return self.__class__.sync_from_stripe_data(
            stripe_customer, api_key=self.default_api_key
        )

    def upcoming_invoice(self, **kwargs):
        """Gets the upcoming preview invoice (singular) for this customer.

        See `Invoice.upcoming() <#djstripe.Invoice.upcoming>`__.

        The ``customer`` argument to the ``upcoming()`` call is automatically set
         by this method.
        """
        from .billing import Invoice

        kwargs["customer"] = self
        return Invoice.upcoming(**kwargs)

    def _attach_objects_post_save_hook(
        self,
        cls,
        data,
        pending_relations=None,
        api_key=djstripe_settings.STRIPE_SECRET_KEY,
    ):
        from .billing import Coupon
        from .payment_methods import DjstripePaymentMethod

        super()._attach_objects_post_save_hook(
            cls, data, pending_relations=pending_relations, api_key=api_key
        )

        save = False

        customer_sources = data.get("sources")
        sources = {}
        if customer_sources:
            # Have to create sources before we handle the default_source
            # We save all of them in the `sources` dict, so that we can find them
            # by id when we look at the default_source (we need the source type).
            for source in customer_sources["data"]:
                obj, _ = DjstripePaymentMethod._get_or_create_source(
                    source, source["object"], api_key=api_key
                )
                sources[source["id"]] = obj

        discount = data.get("discount")
        if discount:
            coupon, _created = Coupon._get_or_create_from_stripe_object(
                discount, "coupon", api_key=api_key
            )
            if coupon and coupon != self.coupon:
                self.coupon = coupon
                save = True
        elif self.coupon:
            self.coupon = None
            save = True

        if save:
            self.save()

    def _attach_objects_hook(
        self, cls, data, current_ids=None, api_key=djstripe_settings.STRIPE_SECRET_KEY
    ):
        # When we save a customer to Stripe, we add a reference to its Django PK
        # in the `django_account` key. If we find that, we re-attach that PK.
        subscriber_key = djstripe_settings.SUBSCRIBER_CUSTOMER_KEY
        if subscriber_key in ("", None):
            # Disabled. Nothing else to do.
            return

        subscriber_id = data.get("metadata", {}).get(subscriber_key)
        if subscriber_id:
            cls = djstripe_settings.get_subscriber_model()
            try:
                # We have to perform a get(), instead of just attaching the PK
                # blindly as the object may have been deleted or not exist.
                # Attempting to save that would cause an IntegrityError.
                self.subscriber = cls.objects.get(pk=subscriber_id)
            except (cls.DoesNotExist, ValueError):
                logger.warning(
                    "Could not find subscriber %r matching customer %r",
                    subscriber_id,
                    self.id,
                )
                self.subscriber = None

    # SYNC methods should be dropped in favor of the master sync infrastructure proposed
    def _sync_invoices(self, **kwargs):
        from .billing import Invoice

        api_key = kwargs.get("api_key") or self.default_api_key
        for stripe_invoice in Invoice.api_list(customer=self.id, **kwargs):
            Invoice.sync_from_stripe_data(stripe_invoice, api_key=api_key)

    def _sync_charges(self, **kwargs):
        api_key = kwargs.get("api_key") or self.default_api_key
        for stripe_charge in Charge.api_list(customer=self.id, **kwargs):
            Charge.sync_from_stripe_data(stripe_charge, api_key=api_key)

    def _sync_cards(self, **kwargs):
        from .payment_methods import Card

        api_key = kwargs.get("api_key") or self.default_api_key
        for stripe_card in Card.api_list(customer=self, **kwargs):
            Card.sync_from_stripe_data(stripe_card, api_key=api_key)

    def _sync_subscriptions(self, **kwargs):
        from .billing import Subscription

        api_key = kwargs.get("api_key") or self.default_api_key
        for stripe_subscription in Subscription.api_list(
            customer=self.id, status="all", **kwargs
        ):
            Subscription.sync_from_stripe_data(stripe_subscription, api_key=api_key)

Attributes

djstripe.models.core.Customer.active_subscriptions property

Returns active subscriptions (subscriptions with an active status that end in the future).

djstripe.models.core.Customer.address = JSONField(null=True, blank=True, help_text="The customer's address.") class-attribute instance-attribute
djstripe.models.core.Customer.balance = StripeQuantumCurrencyAmountField(null=True, blank=True, default=0, help_text="Current balance (in cents), if any, being stored on the customer's account. If negative, the customer has credit to apply to the next invoice. If positive, the customer has an amount owed that will be added to the next invoice. The balance does not refer to any unpaid invoices; it solely takes into account amounts that have yet to be successfully applied to any invoice. This balance is only taken into account for recurring billing purposes (i.e., subscriptions, invoices, invoice items).") class-attribute instance-attribute
djstripe.models.core.Customer.coupon = models.ForeignKey('Coupon', null=True, blank=True, on_delete=models.SET_NULL) class-attribute instance-attribute
djstripe.models.core.Customer.coupon_end = StripeDateTimeField(null=True, blank=True, editable=False, help_text='If a coupon is present and has a limited duration, the date that the discount will end.') class-attribute instance-attribute
djstripe.models.core.Customer.coupon_start = StripeDateTimeField(null=True, blank=True, editable=False, help_text='If a coupon is present, the date at which it was applied.') class-attribute instance-attribute
djstripe.models.core.Customer.credits property

The customer is considered to have credits if their balance is below 0.

djstripe.models.core.Customer.currency = StripeCurrencyCodeField(blank=True, default='', help_text='The currency the customer can be charged in for recurring billing purposes') class-attribute instance-attribute
djstripe.models.core.Customer.customer_payment_methods property

An iterable of all of the customer's payment methods (sources, then legacy cards)

djstripe.models.core.Customer.date_purged = models.DateTimeField(null=True, editable=False) class-attribute instance-attribute
djstripe.models.core.Customer.default_payment_method = StripeForeignKey('PaymentMethod', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', help_text='default payment method used for subscriptions and invoices for the customer.') class-attribute instance-attribute
djstripe.models.core.Customer.default_source = PaymentMethodForeignKey(on_delete=models.SET_NULL, null=True, blank=True, related_name='customers') class-attribute instance-attribute
djstripe.models.core.Customer.deleted = models.BooleanField(default=False, null=True, blank=True, help_text='Whether the Customer instance has been deleted upstream in Stripe or not.') class-attribute instance-attribute
djstripe.models.core.Customer.delinquent = models.BooleanField(null=True, blank=True, default=False, help_text="Whether or not the latest charge for the customer's latest invoice has failed.") class-attribute instance-attribute
djstripe.models.core.Customer.discount = JSONField(null=True, blank=True, help_text='Describes the current discount active on the customer, if there is one.') class-attribute instance-attribute
djstripe.models.core.Customer.email = models.TextField(max_length=5000, default='', blank=True) class-attribute instance-attribute
djstripe.models.core.Customer.expand_fields = ['default_source', 'sources'] class-attribute instance-attribute
djstripe.models.core.Customer.invoice_prefix = models.CharField(default='', blank=True, max_length=255, help_text='The prefix for the customer used to generate unique invoice numbers.') class-attribute instance-attribute
djstripe.models.core.Customer.invoice_settings = JSONField(null=True, blank=True, help_text="The customer's default invoice settings.") class-attribute instance-attribute
djstripe.models.core.Customer.name = models.TextField(max_length=5000, default='', blank=True, help_text="The customer's full name or business name.") class-attribute instance-attribute
djstripe.models.core.Customer.pending_charges property

The customer is considered to have pending charges if their balance is above 0.

djstripe.models.core.Customer.phone = models.TextField(max_length=5000, default='', blank=True, help_text="The customer's phone number.") class-attribute instance-attribute
djstripe.models.core.Customer.preferred_locales = JSONField(null=True, blank=True, help_text="The customer's preferred locales (languages), ordered by preference.") class-attribute instance-attribute
djstripe.models.core.Customer.shipping = JSONField(null=True, blank=True, help_text='Shipping information associated with the customer.') class-attribute instance-attribute
djstripe.models.core.Customer.stripe_class = stripe.Customer class-attribute instance-attribute
djstripe.models.core.Customer.stripe_dashboard_item_name = 'customers' class-attribute instance-attribute
djstripe.models.core.Customer.subscriber = models.ForeignKey(djstripe_settings.get_subscriber_model_string(), blank=True, null=True, on_delete=models.SET_NULL, related_name='djstripe_customers') class-attribute instance-attribute
djstripe.models.core.Customer.subscription property

Shortcut to get this customer's subscription.

:returns: None if the customer has no subscriptions, the subscription if the customer has a subscription. :raises MultipleSubscriptionException: Raised if the customer has multiple subscriptions. In this case, use Customer.subscriptions instead.

djstripe.models.core.Customer.tax_exempt = StripeEnumField(enum=enums.CustomerTaxExempt, default='', help_text='Describes the customer\'s tax exemption status. When set to reverse, invoice and receipt PDFs include the text "Reverse charge".') class-attribute instance-attribute
djstripe.models.core.Customer.valid_subscriptions property

Returns this customer's valid subscriptions (subscriptions that aren't canceled or incomplete_expired).

Classes

djstripe.models.core.Customer.Meta

Bases: Meta

Source code in djstripe/models/core.py
835
836
class Meta(StripeModel.Meta):
    unique_together = ("subscriber", "livemode", "djstripe_owner_account")
Attributes
djstripe.models.core.Customer.Meta.unique_together = ('subscriber', 'livemode', 'djstripe_owner_account') class-attribute instance-attribute

Functions

djstripe.models.core.Customer.__str__()
Source code in djstripe/models/core.py
838
839
840
841
842
def __str__(self):
    if self.subscriber:
        return str(self.subscriber)

    return self.name or self.description or self.id
djstripe.models.core.Customer.add_card(source, set_default=True)

Adds a card to this customer's account.

:param source: Either a token, like the ones returned by our Stripe.js, or a dictionary containing a user's credit card details. Stripe will automatically validate the card. :type source: string, dict :param set_default: Whether or not to set the source as the customer's default source :type set_default: boolean

Source code in djstripe/models/core.py
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
def add_card(self, source, set_default=True):
    """
    Adds a card to this customer's account.

    :param source: Either a token, like the ones returned by our Stripe.js, or a
        dictionary containing a user's credit card details.
        Stripe will automatically validate the card.
    :type source: string, dict
    :param set_default: Whether or not to set the source as the customer's
        default source
    :type set_default: boolean

    """
    from .payment_methods import DjstripePaymentMethod

    stripe_customer = self.api_retrieve()
    new_stripe_payment_method = stripe_customer.sources.create(source=source)

    if set_default:
        stripe_customer.default_source = new_stripe_payment_method["id"]
        stripe_customer.save()

    new_payment_method = DjstripePaymentMethod.from_stripe_object(
        new_stripe_payment_method
    )

    # Change the default source
    if set_default:
        self.default_source = new_payment_method
        self.save()

    return new_payment_method.resolve()
djstripe.models.core.Customer.add_coupon(coupon, idempotency_key=None)

Add a coupon to a Customer.

The coupon can be a Coupon object, or a valid Stripe Coupon ID.

Source code in djstripe/models/core.py
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
def add_coupon(self, coupon, idempotency_key=None):
    """
    Add a coupon to a Customer.

    The coupon can be a Coupon object, or a valid Stripe Coupon ID.
    """
    if isinstance(coupon, StripeModel):
        coupon = coupon.id

    stripe_customer = self.api_retrieve()
    stripe_customer["coupon"] = coupon
    stripe_customer.save(idempotency_key=idempotency_key)
    return self.__class__.sync_from_stripe_data(
        stripe_customer, api_key=self.default_api_key
    )
djstripe.models.core.Customer.add_invoice_item(amount, currency, description=None, discountable=None, invoice=None, metadata=None, subscription=None)

Adds an arbitrary charge or credit to the customer's upcoming invoice. Different than creating a charge. Charges are separate bills that get processed immediately. Invoice items are appended to the customer's next invoice. This is extremely useful when adding surcharges to subscriptions.

:param amount: The amount to charge. :type amount: Decimal. Precision is 2; anything more will be ignored. :param currency: 3-letter ISO code for currency :type currency: string :param description: An arbitrary string. :type description: string :param discountable: Controls whether discounts apply to this invoice item. Defaults to False for prorations or negative invoice items, and True for all other invoice items. :type discountable: boolean :param invoice: An existing invoice to add this invoice item to. When left blank, the invoice item will be added to the next upcoming scheduled invoice. Use this when adding invoice items in response to an invoice.created webhook. You cannot add an invoice item to an invoice that has already been paid, attempted or closed. :type invoice: Invoice or string (invoice ID) :param metadata: A set of key/value pairs useful for storing additional information. :type metadata: dict :param subscription: A subscription to add this invoice item to. When left blank, the invoice item will be be added to the next upcoming scheduled invoice. When set, scheduled invoices for subscriptions other than the specified subscription will ignore the invoice item. Use this when you want to express that an invoice item has been accrued within the context of a particular subscription. :type subscription: Subscription or string (subscription ID)

.. Notes: .. if you're using Customer.add_invoice_item() instead of .. Customer.add_invoice_item(), invoice and subscriptions .. can only be strings

Source code in djstripe/models/core.py
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
def add_invoice_item(
    self,
    amount,
    currency,
    description=None,
    discountable=None,
    invoice=None,
    metadata=None,
    subscription=None,
):
    """
    Adds an arbitrary charge or credit to the customer's upcoming invoice.
    Different than creating a charge. Charges are separate bills that get
    processed immediately. Invoice items are appended to the customer's next
    invoice. This is extremely useful when adding surcharges to subscriptions.

    :param amount: The amount to charge.
    :type amount: Decimal. Precision is 2; anything more will be ignored.
    :param currency: 3-letter ISO code for currency
    :type currency: string
    :param description: An arbitrary string.
    :type description: string
    :param discountable: Controls whether discounts apply to this invoice item.
        Defaults to False for prorations or negative invoice items,
        and True for all other invoice items.
    :type discountable: boolean
    :param invoice: An existing invoice to add this invoice item to.
        When left blank, the invoice item will be added to the next upcoming \
         scheduled invoice. \
         Use this when adding invoice items in response to an \
         ``invoice.created`` webhook. You cannot add an invoice \
        item to an invoice that has already been paid, attempted or closed.
    :type invoice: Invoice or string (invoice ID)
    :param metadata: A set of key/value pairs useful for storing
        additional information.
    :type metadata: dict
    :param subscription: A subscription to add this invoice item to.
        When left blank, the invoice item will be be added to the next upcoming \
        scheduled invoice. When set, scheduled invoices for subscriptions other \
        than the specified subscription will ignore the invoice item. \
        Use this when you want to express that an invoice item has been accrued \
        within the context of a particular subscription.
    :type subscription: Subscription or string (subscription ID)

    .. Notes:
    .. if you're using ``Customer.add_invoice_item()`` instead of
    .. ``Customer.add_invoice_item()``, ``invoice`` and ``subscriptions``
    .. can only be strings
    """
    from .billing import InvoiceItem

    if not isinstance(amount, Decimal):
        raise ValueError("You must supply a decimal value representing dollars.")

    # Convert Invoice to id
    if invoice is not None and isinstance(invoice, StripeModel):
        invoice = invoice.id

    # Convert Subscription to id
    if subscription is not None and isinstance(subscription, StripeModel):
        subscription = subscription.id

    stripe_invoiceitem = InvoiceItem._api_create(
        amount=int(amount * 100),  # Convert dollars into cents
        currency=currency,
        customer=self.id,
        description=description,
        discountable=discountable,
        invoice=invoice,
        metadata=metadata,
        subscription=subscription,
    )

    return InvoiceItem.sync_from_stripe_data(
        stripe_invoiceitem, api_key=self.default_api_key
    )
djstripe.models.core.Customer.add_payment_method(payment_method, set_default=True)

Adds an already existing payment method to this customer's account

:param payment_method: PaymentMethod to be attached to the customer :type payment_method: str, PaymentMethod :param set_default: If true, this will be set as the default_payment_method :type set_default: bool :rtype: PaymentMethod

Source code in djstripe/models/core.py
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
def add_payment_method(self, payment_method, set_default=True):
    """
    Adds an already existing payment method to this customer's account

    :param payment_method: PaymentMethod to be attached to the customer
    :type payment_method: str, PaymentMethod
    :param set_default: If true, this will be set as the default_payment_method
    :type set_default: bool
    :rtype: PaymentMethod
    """
    from .payment_methods import PaymentMethod

    stripe_customer = self.api_retrieve()
    payment_method = PaymentMethod.attach(payment_method, stripe_customer)

    if set_default:
        stripe_customer["invoice_settings"][
            "default_payment_method"
        ] = payment_method.id
        stripe_customer.save()

        # Refresh self from the stripe customer, this should have two effects:
        # 1) sets self.default_payment_method (we rely on logic in
        # Customer._manipulate_stripe_object_hook to do this)
        # 2) updates self.invoice_settings.default_payment_methods
        self.sync_from_stripe_data(stripe_customer, api_key=self.default_api_key)
        self.refresh_from_db()

    return payment_method
djstripe.models.core.Customer.charge(amount, *, application_fee=None, source=None, **kwargs)

Creates a charge for this customer.

:param amount: The amount to charge. :type amount: Decimal. Precision is 2; anything more will be ignored. :param source: The source to use for this charge. Must be a source attributed to this customer. If None, the customer's default source is used. Can be either the id of the source or the source object itself. :type source: string, Source

Source code in djstripe/models/core.py
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
def charge(
    self,
    amount: Decimal,
    *,
    application_fee: Decimal = None,
    source: Union[str, StripeModel] = None,
    **kwargs,
) -> Charge:
    """
    Creates a charge for this customer.

    :param amount: The amount to charge.
    :type amount: Decimal. Precision is 2; anything more will be ignored.
    :param source: The source to use for this charge.
        Must be a source attributed to this customer. If None, the customer's
        default source is used. Can be either the id of the source or
        the source object itself.
    :type source: string, Source
    """

    if not isinstance(amount, Decimal):
        raise ValueError("You must supply a decimal value representing dollars.")

    # Convert Source to id
    if source and isinstance(source, StripeModel):
        source = source.id

    stripe_charge = Charge._api_create(
        customer=self.id,
        amount=int(amount * 100),  # Convert dollars into cents
        application_fee=(
            int(application_fee * 100) if application_fee else None
        ),  # Convert dollars into cents
        source=source,
        **kwargs,
    )

    api_key = kwargs.get("api_key") or self.default_api_key
    return Charge.sync_from_stripe_data(stripe_charge, api_key=api_key)
djstripe.models.core.Customer.create(subscriber, idempotency_key=None, stripe_account=None) classmethod
Source code in djstripe/models/core.py
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
@classmethod
def create(cls, subscriber, idempotency_key=None, stripe_account=None):
    metadata = {}
    subscriber_key = djstripe_settings.SUBSCRIBER_CUSTOMER_KEY
    if subscriber_key not in ("", None):
        metadata[subscriber_key] = subscriber.pk

    try:
        # if subscriber table has a get_full_name() method, use it as name
        # ref django.contrib.auth.models.User.get_full_name
        name = subscriber.get_full_name()
    except AttributeError:
        name = None

    stripe_customer = cls._api_create(
        email=subscriber.email,
        name=name,
        idempotency_key=idempotency_key,
        metadata=metadata,
        stripe_account=stripe_account,
    )
    customer, created = cls.objects.get_or_create(
        id=stripe_customer["id"],
        defaults={
            "subscriber": subscriber,
            "livemode": stripe_customer["livemode"],
            "balance": stripe_customer.get("balance", 0),
            "delinquent": stripe_customer.get("delinquent", False),
        },
    )

    return customer
djstripe.models.core.Customer.get_or_create(subscriber, livemode=djstripe_settings.STRIPE_LIVE_MODE, stripe_account=None) classmethod

Get or create a dj-stripe customer.

:param subscriber: The subscriber model instance for which to get or create a customer. :type subscriber: User

:param livemode: Whether to get the subscriber in live or test mode. :type livemode: bool

Source code in djstripe/models/core.py
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
@classmethod
def get_or_create(
    cls,
    subscriber,
    livemode=djstripe_settings.STRIPE_LIVE_MODE,
    stripe_account=None,
):
    """
    Get or create a dj-stripe customer.

    :param subscriber: The subscriber model instance for which to get or
        create a customer.
    :type subscriber: User

    :param livemode: Whether to get the subscriber in live or test mode.
    :type livemode: bool
    """

    try:
        return cls.objects.get(subscriber=subscriber, livemode=livemode), False
    except cls.DoesNotExist:
        action = f"create:{subscriber.pk}"
        idempotency_key = djstripe_settings.get_idempotency_key(
            "customer", action, livemode
        )
        return (
            cls.create(
                subscriber,
                idempotency_key=idempotency_key,
                stripe_account=stripe_account,
            ),
            True,
        )
djstripe.models.core.Customer.has_any_active_subscription()

Checks to see if this customer has an active subscription to any plan.

:returns: True if there exists an active subscription, False otherwise.

Source code in djstripe/models/core.py
1247
1248
1249
1250
1251
1252
1253
1254
def has_any_active_subscription(self):
    """
    Checks to see if this customer has an active subscription to any plan.

    :returns: True if there exists an active subscription, False otherwise.
    """

    return len(self._get_valid_subscriptions()) != 0
djstripe.models.core.Customer.is_subscribed_to(product)

Checks to see if this customer has an active subscription to the given product.

:param product: The product for which to check for an active subscription. :type product: Product or string (product ID)

:returns: True if there exists an active subscription, False otherwise.

Source code in djstripe/models/core.py
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
def is_subscribed_to(self, product: Union[Product, str]) -> bool:
    """
    Checks to see if this customer has an active subscription to the given product.

    :param product: The product for which to check for an active subscription.
    :type product: Product or string (product ID)

    :returns: True if there exists an active subscription, False otherwise.
    """

    if isinstance(product, StripeModel):
        product = product.id

    for subscription in self._get_valid_subscriptions():
        for item in subscription.items.all():
            if item.price and item.price.product.id == product:
                return True
    return False
djstripe.models.core.Customer.purge()

Customers are soft deleted as deleted customers are still accessible by the Stripe API and sync for all RelatedModels would fail

Source code in djstripe/models/core.py
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
def purge(self):
    """Customers are soft deleted as deleted customers are still accessible by the
    Stripe API and sync for all RelatedModels would fail"""
    try:
        self._api_delete()
    except InvalidRequestError as exc:
        if "No such customer:" in str(exc):
            # The exception was thrown because the stripe customer was already
            # deleted on the stripe side, ignore the exception
            pass
        else:
            # The exception was raised for another reason, re-raise it
            raise

    # toggle the deleted flag on Customer to indicate it has been
    # deleted upstream in Stripe
    self.deleted = True

    if self.subscriber:
        # Delete the idempotency key used by Customer.create()
        # So re-creating a customer for this subscriber before the key expires
        # doesn't return the older Customer data
        idempotency_key_action = f"customer:create:{self.subscriber.pk}"
        IdempotencyKey.objects.filter(action=idempotency_key_action).delete()

    self.subscriber = None

    # Remove sources
    self.default_source = None
    for source in self.legacy_cards.all():
        source.remove()

    for source in self.sources.all():
        source.detach()

    self.date_purged = timezone.now()
    self.save()
djstripe.models.core.Customer.retry_unpaid_invoices(**kwargs)

Attempt to retry collecting payment on the customer's unpaid invoices.

Source code in djstripe/models/core.py
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
def retry_unpaid_invoices(self, **kwargs):
    """Attempt to retry collecting payment on the customer's unpaid invoices."""

    self._sync_invoices()
    for invoice in self.invoices.filter(auto_advance=True).exclude(status="paid"):
        try:
            invoice.retry(**kwargs)  # Always retry unpaid invoices
        except InvalidRequestError as exc:
            if str(exc) != "Invoice is already paid":
                raise
djstripe.models.core.Customer.send_invoice(**kwargs)

Pay and send the customer's latest invoice.

:returns: True if an invoice was able to be created and paid, False otherwise (typically if there was nothing to invoice).

Source code in djstripe/models/core.py
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
def send_invoice(self, **kwargs):
    """
    Pay and send the customer's latest invoice.

    :returns: True if an invoice was able to be created and paid, False otherwise
        (typically if there was nothing to invoice).
    """
    from .billing import Invoice

    try:
        invoice = Invoice._api_create(customer=self.id)
        invoice.pay(**kwargs)
        return True
    except InvalidRequestError:  # TODO: Check this for a more
        #                           specific error message.
        return False  # There was nothing to invoice
djstripe.models.core.Customer.subscribe(*, items=None, price=None, plan=None, **kwargs)

Subscribes this customer to all the prices or plans in the items dict (Recommended).

:param items: A list of up to 20 subscription items, each with an attached price :type list: :param items: A dictionary of Plan (or Plan ID) or Price (or Price ID) :type dict: The price or plan to which to subscribe the customer.

:param price: The price to which to subscribe the customer. :type price: Price or string (price ID)

:param plan: The plan to which to subscribe the customer. :type plan: Plan or string (plan ID)

Source code in djstripe/models/core.py
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
def subscribe(self, *, items=None, price=None, plan=None, **kwargs):
    """
    Subscribes this customer to all the prices or plans in the items dict (Recommended).

    :param items: A list of up to 20 subscription items, each with an attached price
    :type list:
        :param items: A dictionary of Plan (or Plan ID) or Price (or Price ID)
        :type dict:  The price or plan to which to subscribe the customer.

    :param price: The price to which to subscribe the customer.
    :type price: Price or string (price ID)

    :param plan: The plan to which to subscribe the customer.
    :type plan: Plan or string (plan ID)
    """
    from .billing import Subscription

    if (items and price) or (items and plan) or (price and plan):
        raise TypeError("Please define only one of items, price or plan arguments.")

    if items is None:
        _items = [{"price": price}]
    else:
        _items = []
        for item in items:
            price = item.get("price", "")
            plan = item.get("plan", "")
            price, kwargs = _sanitise_price(price, plan, **kwargs)
            if "price" in item:
                _items.append({"price": price})
            if "plan" in item:
                _items.append({"plan": price})

    stripe_subscription = Subscription._api_create(
        items=_items, customer=self.id, **kwargs
    )

    api_key = kwargs.get("api_key") or self.default_api_key
    return Subscription.sync_from_stripe_data(stripe_subscription, api_key=api_key)
djstripe.models.core.Customer.upcoming_invoice(**kwargs)

Gets the upcoming preview invoice (singular) for this customer.

See Invoice.upcoming() <#djstripe.Invoice.upcoming>__.

The customer argument to the upcoming() call is automatically set by this method.

Source code in djstripe/models/core.py
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
def upcoming_invoice(self, **kwargs):
    """Gets the upcoming preview invoice (singular) for this customer.

    See `Invoice.upcoming() <#djstripe.Invoice.upcoming>`__.

    The ``customer`` argument to the ``upcoming()`` call is automatically set
     by this method.
    """
    from .billing import Invoice

    kwargs["customer"] = self
    return Invoice.upcoming(**kwargs)

djstripe.models.core.Dispute

Bases: StripeModel

A dispute occurs when a customer questions your charge with their card issuer. When this happens, you're given the opportunity to respond to the dispute with evidence that shows that the charge is legitimate

Stripe documentation: https://stripe.com/docs/api?lang=python#disputes

Source code in djstripe/models/core.py
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
class Dispute(StripeModel):
    """
    A dispute occurs when a customer questions your charge with their
    card issuer. When this happens, you're given the opportunity to
    respond to the dispute with evidence that shows that the charge is legitimate

    Stripe documentation: https://stripe.com/docs/api?lang=python#disputes
    """

    stripe_class = stripe.Dispute
    stripe_dashboard_item_name = "payments"

    amount = StripeQuantumCurrencyAmountField(
        help_text=(
            "Disputed amount (in cents). Usually the amount of the charge, "
            "but can differ "
            "(usually because of currency fluctuation or because only part of "
            "the order is disputed)."
        )
    )
    balance_transaction = StripeForeignKey(
        "BalanceTransaction",
        null=True,
        on_delete=models.CASCADE,
        related_name="disputes",
        help_text=(
            "Balance transaction that describes the impact on your account balance."
        ),
    )
    balance_transactions = JSONField(
        default=list,
        help_text=(
            "List of 0, 1 or 2 Balance Transactions that show funds withdrawn and"
            " reinstated to your Stripe account as a result of this dispute."
        ),
    )
    # charge is nullable to avoid infinite sync as Charge model has a dispute field as well
    charge = StripeForeignKey(
        "Charge",
        null=True,
        on_delete=models.CASCADE,
        related_name="disputes",
        help_text="The charge that was disputed",
    )
    currency = StripeCurrencyCodeField()
    evidence = JSONField(help_text="Evidence provided to respond to a dispute.")
    evidence_details = JSONField(help_text="Information about the evidence submission.")
    is_charge_refundable = models.BooleanField(
        help_text=(
            "If true, it is still possible to refund the disputed payment. "
            "Once the payment has been fully refunded, no further funds will "
            "be withdrawn from your Stripe account as a result of this dispute."
        )
    )
    payment_intent = StripeForeignKey(
        "PaymentIntent",
        null=True,
        on_delete=models.CASCADE,
        related_name="disputes",
        help_text="The PaymentIntent that was disputed",
    )
    reason = StripeEnumField(enum=enums.DisputeReason)
    status = StripeEnumField(enum=enums.DisputeStatus)

    def __str__(self):
        amount = get_friendly_currency_amount(self.amount / 100, self.currency)
        status = enums.DisputeStatus.humanize(self.status)
        return f"{amount} ({status}) "

    def get_stripe_dashboard_url(self) -> str:
        """Get the stripe dashboard url for this object."""
        return (
            f"{self._get_base_stripe_dashboard_url()}"
            f"{self.stripe_dashboard_item_name}/{self.payment_intent.id}"
        )

    def _attach_objects_post_save_hook(
        self,
        cls,
        data,
        pending_relations=None,
        api_key=djstripe_settings.STRIPE_SECRET_KEY,
    ):
        super()._attach_objects_post_save_hook(
            cls, data, pending_relations=pending_relations, api_key=api_key
        )

        # Retrieve and save files from the dispute.evidence object.
        # todo find a better way of retrieving and syncing File Type fields from Dispute object
        for field in (
            "cancellation_policy",
            "customer_communication",
            "customer_signature",
            "duplicate_charge_documentation",
            "receipt",
            "refund_policy",
            "service_documentation",
            "shipping_documentation",
            "uncategorized_file",
        ):
            file_upload_id = self.evidence.get(field, None)
            if file_upload_id:
                try:
                    File.sync_from_stripe_data(
                        File(id=file_upload_id).api_retrieve(api_key=api_key),
                        api_key=api_key,
                    )
                except stripe.error.PermissionError:
                    # No permission to retrieve the data with the key
                    # Log a warning message
                    logger.warning(
                        "No permission to retrieve the File Evidence Object."
                    )
                except stripe.error.InvalidRequestError:
                    raise

        # iterate and sync every balance transaction
        for stripe_balance_transaction in self.balance_transactions:
            BalanceTransaction.sync_from_stripe_data(
                stripe_balance_transaction, api_key=api_key
            )

Attributes

djstripe.models.core.Dispute.amount = StripeQuantumCurrencyAmountField(help_text='Disputed amount (in cents). Usually the amount of the charge, but can differ (usually because of currency fluctuation or because only part of the order is disputed).') class-attribute instance-attribute
djstripe.models.core.Dispute.balance_transaction = StripeForeignKey('BalanceTransaction', null=True, on_delete=models.CASCADE, related_name='disputes', help_text='Balance transaction that describes the impact on your account balance.') class-attribute instance-attribute
djstripe.models.core.Dispute.balance_transactions = JSONField(default=list, help_text='List of 0, 1 or 2 Balance Transactions that show funds withdrawn and reinstated to your Stripe account as a result of this dispute.') class-attribute instance-attribute
djstripe.models.core.Dispute.charge = StripeForeignKey('Charge', null=True, on_delete=models.CASCADE, related_name='disputes', help_text='The charge that was disputed') class-attribute instance-attribute
djstripe.models.core.Dispute.currency = StripeCurrencyCodeField() class-attribute instance-attribute
djstripe.models.core.Dispute.evidence = JSONField(help_text='Evidence provided to respond to a dispute.') class-attribute instance-attribute
djstripe.models.core.Dispute.evidence_details = JSONField(help_text='Information about the evidence submission.') class-attribute instance-attribute
djstripe.models.core.Dispute.is_charge_refundable = models.BooleanField(help_text='If true, it is still possible to refund the disputed payment. Once the payment has been fully refunded, no further funds will be withdrawn from your Stripe account as a result of this dispute.') class-attribute instance-attribute
djstripe.models.core.Dispute.payment_intent = StripeForeignKey('PaymentIntent', null=True, on_delete=models.CASCADE, related_name='disputes', help_text='The PaymentIntent that was disputed') class-attribute instance-attribute
djstripe.models.core.Dispute.reason = StripeEnumField(enum=enums.DisputeReason) class-attribute instance-attribute
djstripe.models.core.Dispute.status = StripeEnumField(enum=enums.DisputeStatus) class-attribute instance-attribute
djstripe.models.core.Dispute.stripe_class = stripe.Dispute class-attribute instance-attribute
djstripe.models.core.Dispute.stripe_dashboard_item_name = 'payments' class-attribute instance-attribute

Functions

djstripe.models.core.Dispute.__str__()
Source code in djstripe/models/core.py
1522
1523
1524
1525
def __str__(self):
    amount = get_friendly_currency_amount(self.amount / 100, self.currency)
    status = enums.DisputeStatus.humanize(self.status)
    return f"{amount} ({status}) "
djstripe.models.core.Dispute.get_stripe_dashboard_url()

Get the stripe dashboard url for this object.

Source code in djstripe/models/core.py
1527
1528
1529
1530
1531
1532
def get_stripe_dashboard_url(self) -> str:
    """Get the stripe dashboard url for this object."""
    return (
        f"{self._get_base_stripe_dashboard_url()}"
        f"{self.stripe_dashboard_item_name}/{self.payment_intent.id}"
    )

djstripe.models.core.Event

Bases: StripeModel

Events are Stripe's way of letting you know when something interesting happens in your account. When an interesting event occurs, a new Event object is created and POSTed to the configured webhook URL if the Event type matches.

Stripe documentation: https://stripe.com/docs/api/events?lang=python

Source code in djstripe/models/core.py
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
class Event(StripeModel):
    """
    Events are Stripe's way of letting you know when something interesting
    happens in your account.
    When an interesting event occurs, a new Event object is created and POSTed
    to the configured webhook URL if the Event type matches.

    Stripe documentation: https://stripe.com/docs/api/events?lang=python
    """

    stripe_class = stripe.Event
    stripe_dashboard_item_name = "events"

    api_version = models.CharField(
        max_length=64,
        blank=True,
        help_text=(
            "the API version at which the event data was "
            "rendered. Blank for old entries only, all new entries will have this value"
        ),
    )
    data = JSONField(
        help_text=(
            "data received at webhook. data should be considered to be garbage "
            "until validity check is run and valid flag is set"
        )
    )
    request_id = models.CharField(
        max_length=50,
        help_text=(
            "Information about the request that triggered this event, "
            "for traceability purposes. If empty string then this is an old entry "
            "without that data. If Null then this is not an old entry, but a Stripe "
            "'automated' event with no associated request."
        ),
        default="",
        blank=True,
    )
    idempotency_key = models.TextField(default="", blank=True)
    type = models.CharField(max_length=250, help_text="Stripe's event description code")

    def __str__(self):
        return f"type={self.type}, id={self.id}"

    def _attach_objects_hook(
        self, cls, data, current_ids=None, api_key=djstripe_settings.STRIPE_SECRET_KEY
    ):
        if self.api_version is None:
            # as of api version 2017-02-14, the account.application.deauthorized
            # event sends None as api_version.
            # If we receive that, store an empty string instead.
            # Remove this hack if this gets fixed upstream.
            self.api_version = ""

        request_obj = data.get("request", None)
        if isinstance(request_obj, dict):
            # Format as of 2017-05-25
            self.request_id = request_obj.get("id") or ""
            self.idempotency_key = request_obj.get("idempotency_key") or ""
        else:
            # Format before 2017-05-25
            self.request_id = request_obj or ""

    @classmethod
    def process(cls, data, api_key=djstripe_settings.STRIPE_SECRET_KEY):
        qs = cls.objects.filter(id=data["id"])
        if qs.exists():
            return qs.first()

        # Rollback any DB operations in the case of failure so
        # we will retry creating and processing the event the
        # next time the webhook fires.
        with transaction.atomic():
            # process the event and create an Event Object
            ret = cls._create_from_stripe_object(data, api_key=api_key)
            ret.invoke_webhook_handlers()
            return ret

    def invoke_webhook_handlers(self):
        """
        Invokes any webhook handlers that have been registered for this event
        based on event type or event sub-type.

        See event handlers registered in the ``djstripe.event_handlers`` module
        (or handlers registered in djstripe plugins or contrib packages).
        """

        webhooks.call_handlers(event=self)

        signal = WEBHOOK_SIGNALS.get(self.type)
        if signal:
            return signal.send(sender=Event, event=self)

    @cached_property
    def parts(self):
        """Gets the event category/verb as a list of parts."""
        return str(self.type).split(".")

    @cached_property
    def category(self):
        """Gets the event category string (e.g. 'customer')."""
        return self.parts[0]

    @cached_property
    def verb(self):
        """Gets the event past-tense verb string (e.g. 'updated')."""
        return ".".join(self.parts[1:])

    @property
    def customer(self):
        data = self.data["object"]
        if data["object"] == "customer":
            customer_id = get_id_from_stripe_data(data.get("id"))
        else:
            customer_id = get_id_from_stripe_data(data.get("customer"))

        if customer_id:
            return Customer._get_or_retrieve(
                id=customer_id,
                stripe_account=getattr(self.djstripe_owner_account, "id", None),
                api_key=self.default_api_key,
            )

Attributes

djstripe.models.core.Event.api_version = models.CharField(max_length=64, blank=True, help_text='the API version at which the event data was rendered. Blank for old entries only, all new entries will have this value') class-attribute instance-attribute
djstripe.models.core.Event.customer property
djstripe.models.core.Event.data = JSONField(help_text='data received at webhook. data should be considered to be garbage until validity check is run and valid flag is set') class-attribute instance-attribute
djstripe.models.core.Event.idempotency_key = models.TextField(default='', blank=True) class-attribute instance-attribute
djstripe.models.core.Event.request_id = models.CharField(max_length=50, help_text="Information about the request that triggered this event, for traceability purposes. If empty string then this is an old entry without that data. If Null then this is not an old entry, but a Stripe 'automated' event with no associated request.", default='', blank=True) class-attribute instance-attribute
djstripe.models.core.Event.stripe_class = stripe.Event class-attribute instance-attribute
djstripe.models.core.Event.stripe_dashboard_item_name = 'events' class-attribute instance-attribute
djstripe.models.core.Event.type = models.CharField(max_length=250, help_text="Stripe's event description code") class-attribute instance-attribute

Functions

djstripe.models.core.Event.__str__()
Source code in djstripe/models/core.py
1622
1623
def __str__(self):
    return f"type={self.type}, id={self.id}"
djstripe.models.core.Event.category()

Gets the event category string (e.g. 'customer').

Source code in djstripe/models/core.py
1679
1680
1681
1682
@cached_property
def category(self):
    """Gets the event category string (e.g. 'customer')."""
    return self.parts[0]
djstripe.models.core.Event.invoke_webhook_handlers()

Invokes any webhook handlers that have been registered for this event based on event type or event sub-type.

See event handlers registered in the djstripe.event_handlers module (or handlers registered in djstripe plugins or contrib packages).

Source code in djstripe/models/core.py
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
def invoke_webhook_handlers(self):
    """
    Invokes any webhook handlers that have been registered for this event
    based on event type or event sub-type.

    See event handlers registered in the ``djstripe.event_handlers`` module
    (or handlers registered in djstripe plugins or contrib packages).
    """

    webhooks.call_handlers(event=self)

    signal = WEBHOOK_SIGNALS.get(self.type)
    if signal:
        return signal.send(sender=Event, event=self)
djstripe.models.core.Event.parts()

Gets the event category/verb as a list of parts.

Source code in djstripe/models/core.py
1674
1675
1676
1677
@cached_property
def parts(self):
    """Gets the event category/verb as a list of parts."""
    return str(self.type).split(".")
djstripe.models.core.Event.process(data, api_key=djstripe_settings.STRIPE_SECRET_KEY) classmethod
Source code in djstripe/models/core.py
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
@classmethod
def process(cls, data, api_key=djstripe_settings.STRIPE_SECRET_KEY):
    qs = cls.objects.filter(id=data["id"])
    if qs.exists():
        return qs.first()

    # Rollback any DB operations in the case of failure so
    # we will retry creating and processing the event the
    # next time the webhook fires.
    with transaction.atomic():
        # process the event and create an Event Object
        ret = cls._create_from_stripe_object(data, api_key=api_key)
        ret.invoke_webhook_handlers()
        return ret
djstripe.models.core.Event.verb()

Gets the event past-tense verb string (e.g. 'updated').

Source code in djstripe/models/core.py
1684
1685
1686
1687
@cached_property
def verb(self):
    """Gets the event past-tense verb string (e.g. 'updated')."""
    return ".".join(self.parts[1:])

djstripe.models.core.File

Bases: StripeModel

This is an object representing a file hosted on Stripe's servers. The file may have been uploaded by yourself using the create file request (for example, when uploading dispute evidence) or it may have been created by Stripe (for example, the results of a Sigma scheduled query).

Stripe documentation: https://stripe.com/docs/api/files?lang=python

Source code in djstripe/models/core.py
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
class File(StripeModel):
    """
    This is an object representing a file hosted on Stripe's servers.
    The file may have been uploaded by yourself using the create file request
    (for example, when uploading dispute evidence) or it may have been created by
    Stripe (for example, the results of a Sigma scheduled query).

    Stripe documentation: https://stripe.com/docs/api/files?lang=python
    """

    stripe_class = stripe.File

    filename = models.CharField(
        max_length=255,
        help_text="A filename for the file, suitable for saving to a filesystem.",
    )
    purpose = StripeEnumField(
        enum=enums.FilePurpose, help_text="The purpose of the uploaded file."
    )
    size = models.IntegerField(help_text="The size in bytes of the file upload object.")
    type = StripeEnumField(
        enum=enums.FileType, help_text="The type of the file returned."
    )
    url = models.CharField(
        max_length=200,
        help_text="A read-only URL where the uploaded file can be accessed.",
    )

    @classmethod
    def is_valid_object(cls, data):
        return data and data.get("object") in ("file", "file_upload")

    def __str__(self):
        return f"{self.filename}, {enums.FilePurpose.humanize(self.purpose)}"

Attributes

djstripe.models.core.File.filename = models.CharField(max_length=255, help_text='A filename for the file, suitable for saving to a filesystem.') class-attribute instance-attribute
djstripe.models.core.File.purpose = StripeEnumField(enum=enums.FilePurpose, help_text='The purpose of the uploaded file.') class-attribute instance-attribute
djstripe.models.core.File.size = models.IntegerField(help_text='The size in bytes of the file upload object.') class-attribute instance-attribute
djstripe.models.core.File.stripe_class = stripe.File class-attribute instance-attribute
djstripe.models.core.File.type = StripeEnumField(enum=enums.FileType, help_text='The type of the file returned.') class-attribute instance-attribute
djstripe.models.core.File.url = models.CharField(max_length=200, help_text='A read-only URL where the uploaded file can be accessed.') class-attribute instance-attribute

Functions

djstripe.models.core.File.__str__()
Source code in djstripe/models/core.py
1737
1738
def __str__(self):
    return f"{self.filename}, {enums.FilePurpose.humanize(self.purpose)}"
djstripe.models.core.File.is_valid_object(data) classmethod
Source code in djstripe/models/core.py
1733
1734
1735
@classmethod
def is_valid_object(cls, data):
    return data and data.get("object") in ("file", "file_upload")

Bases: StripeModel

To share the contents of a File object with non-Stripe users, you can create a FileLink. FileLinks contain a URL that can be used to retrieve the contents of the file without authentication.

Stripe documentation: https://stripe.com/docs/api/file_links?lang=python

Source code in djstripe/models/core.py
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
class FileLink(StripeModel):
    """
    To share the contents of a File object with non-Stripe users,
    you can create a FileLink. FileLinks contain a URL that can be used
    to retrieve the contents of the file without authentication.

    Stripe documentation: https://stripe.com/docs/api/file_links?lang=python
    """

    stripe_class = stripe.FileLink

    expires_at = StripeDateTimeField(
        null=True, blank=True, help_text="Time at which the link expires."
    )
    file = StripeForeignKey("File", on_delete=models.CASCADE)
    url = models.URLField(help_text="The publicly accessible URL to download the file.")

    def __str__(self):
        return f"{self.file.filename}, {self.url}"
djstripe.models.core.FileLink.expires_at = StripeDateTimeField(null=True, blank=True, help_text='Time at which the link expires.') class-attribute instance-attribute
djstripe.models.core.FileLink.file = StripeForeignKey('File', on_delete=models.CASCADE) class-attribute instance-attribute
djstripe.models.core.FileLink.stripe_class = stripe.FileLink class-attribute instance-attribute
djstripe.models.core.FileLink.url = models.URLField(help_text='The publicly accessible URL to download the file.') class-attribute instance-attribute
djstripe.models.core.FileLink.__str__()
Source code in djstripe/models/core.py
1764
1765
def __str__(self):
    return f"{self.file.filename}, {self.url}"

djstripe.models.core.Mandate

Bases: StripeModel

A Mandate is a record of the permission a customer has given you to debit their payment method.

https://stripe.com/docs/api/mandates

Source code in djstripe/models/core.py
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
class Mandate(StripeModel):
    """
    A Mandate is a record of the permission a customer has given you to debit their payment method.

    https://stripe.com/docs/api/mandates
    """

    stripe_class = stripe.Mandate

    customer_acceptance = JSONField(
        help_text="Details about the customer's acceptance of the mandate."
    )
    payment_method = StripeForeignKey("paymentmethod", on_delete=models.CASCADE)
    payment_method_details = JSONField(
        help_text="Additional mandate information specific to the payment method type."
    )
    status = StripeEnumField(
        enum=enums.MandateStatus,
        help_text=(
            "The status of the mandate, which indicates whether it can be used to"
            " initiate a payment."
        ),
    )
    type = StripeEnumField(
        enum=enums.MandateType,
        help_text=(
            "The status of the mandate, which indicates whether it can be used to"
            " initiate a payment."
        ),
    )
    multi_use = JSONField(
        null=True,
        blank=True,
        help_text=(
            "If this is a `multi_use` mandate, this hash contains details about the"
            " mandate."
        ),
    )
    single_use = JSONField(
        null=True,
        blank=True,
        help_text=(
            "If this is a `single_use` mandate, this hash contains details about the"
            " mandate."
        ),
    )

Attributes

djstripe.models.core.Mandate.customer_acceptance = JSONField(help_text="Details about the customer's acceptance of the mandate.") class-attribute instance-attribute
djstripe.models.core.Mandate.multi_use = JSONField(null=True, blank=True, help_text='If this is a `multi_use` mandate, this hash contains details about the mandate.') class-attribute instance-attribute
djstripe.models.core.Mandate.payment_method = StripeForeignKey('paymentmethod', on_delete=models.CASCADE) class-attribute instance-attribute
djstripe.models.core.Mandate.payment_method_details = JSONField(help_text='Additional mandate information specific to the payment method type.') class-attribute instance-attribute
djstripe.models.core.Mandate.single_use = JSONField(null=True, blank=True, help_text='If this is a `single_use` mandate, this hash contains details about the mandate.') class-attribute instance-attribute
djstripe.models.core.Mandate.status = StripeEnumField(enum=enums.MandateStatus, help_text='The status of the mandate, which indicates whether it can be used to initiate a payment.') class-attribute instance-attribute
djstripe.models.core.Mandate.stripe_class = stripe.Mandate class-attribute instance-attribute
djstripe.models.core.Mandate.type = StripeEnumField(enum=enums.MandateType, help_text='The status of the mandate, which indicates whether it can be used to initiate a payment.') class-attribute instance-attribute

djstripe.models.core.PaymentIntent

Bases: StripeModel

A PaymentIntent guides you through the process of collecting a payment from your customer. We recommend that you create exactly one PaymentIntent for each order or customer session in your system. You can reference the PaymentIntent later to see the history of payment attempts for a particular session.

A PaymentIntent transitions through multiple statuses throughout its lifetime as it interfaces with Stripe.js to perform authentication flows and ultimately creates at most one successful charge.

Stripe documentation: https://stripe.com/docs/api?lang=python#payment_intents

Source code in djstripe/models/core.py
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
class PaymentIntent(StripeModel):
    """
    A PaymentIntent guides you through the process of collecting a payment
    from your customer. We recommend that you create exactly one PaymentIntent for each order
    or customer session in your system. You can reference the PaymentIntent later to
    see the history of payment attempts for a particular session.

    A PaymentIntent transitions through multiple statuses throughout its lifetime as
    it interfaces with Stripe.js to perform authentication flows and ultimately
    creates at most one successful charge.

    Stripe documentation: https://stripe.com/docs/api?lang=python#payment_intents
    """

    stripe_class = stripe.PaymentIntent
    stripe_dashboard_item_name = "payments"

    amount = StripeQuantumCurrencyAmountField(
        help_text="Amount (in cents) intended to be collected by this PaymentIntent."
    )
    amount_capturable = StripeQuantumCurrencyAmountField(
        help_text="Amount (in cents) that can be captured from this PaymentIntent."
    )
    amount_received = StripeQuantumCurrencyAmountField(
        help_text="Amount (in cents) that was collected by this PaymentIntent."
    )
    # application
    # application_fee_amount
    canceled_at = StripeDateTimeField(
        null=True,
        blank=True,
        default=None,
        help_text=(
            "Populated when status is canceled, this is the time at which the "
            "PaymentIntent was canceled. Measured in seconds since the Unix epoch."
        ),
    )

    cancellation_reason = StripeEnumField(
        enum=enums.PaymentIntentCancellationReason,
        blank=True,
        help_text=(
            "Reason for cancellation of this PaymentIntent, either user-provided "
            "(duplicate, fraudulent, requested_by_customer, or abandoned) or "
            "generated by Stripe internally (failed_invoice, void_invoice, "
            "or automatic)."
        ),
    )
    capture_method = StripeEnumField(
        enum=enums.CaptureMethod,
        help_text="Capture method of this PaymentIntent, one of automatic or manual.",
    )
    client_secret = models.TextField(
        max_length=5000,
        help_text=(
            "The client secret of this PaymentIntent. "
            "Used for client-side retrieval using a publishable key."
        ),
    )
    confirmation_method = StripeEnumField(
        enum=enums.ConfirmationMethod,
        help_text=(
            "Confirmation method of this PaymentIntent, one of manual or automatic."
        ),
    )
    currency = StripeCurrencyCodeField()
    customer = StripeForeignKey(
        "Customer",
        null=True,
        on_delete=models.CASCADE,
        help_text="Customer this PaymentIntent is for if one exists.",
    )
    description = models.TextField(
        max_length=1000,
        default="",
        blank=True,
        help_text=(
            "An arbitrary string attached to the object. "
            "Often useful for displaying to users."
        ),
    )
    last_payment_error = JSONField(
        null=True,
        blank=True,
        help_text=(
            "The payment error encountered in the previous PaymentIntent confirmation."
        ),
    )
    next_action = JSONField(
        null=True,
        blank=True,
        help_text=(
            "If present, this property tells you what actions you need to take "
            "in order for your customer to fulfill a payment using the provided source."
        ),
    )
    on_behalf_of = StripeForeignKey(
        "Account",
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        help_text=(
            "The account (if any) for which the funds of the "
            "PaymentIntent are intended."
        ),
        related_name="payment_intents",
    )
    payment_method = StripeForeignKey(
        "PaymentMethod",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        help_text="Payment method used in this PaymentIntent.",
    )
    payment_method_types = JSONField(
        help_text=(
            "The list of payment method types (e.g. card) that this "
            "PaymentIntent is allowed to use."
        )
    )
    receipt_email = models.CharField(
        blank=True,
        max_length=255,
        help_text=(
            "Email address that the receipt for the resulting payment will be sent to."
        ),
    )
    # TODO: Add `review` field after we add Review model.
    setup_future_usage = StripeEnumField(
        enum=enums.IntentUsage,
        null=True,
        blank=True,
        help_text=(
            "Indicates that you intend to make future payments with this "
            "PaymentIntent's payment method. "
            "If present, the payment method used with this PaymentIntent can "
            "be attached to a Customer, even after the transaction completes. "
            "Use `on_session` if you intend to only reuse the payment method "
            "when your customer is present in your checkout flow. Use `off_session` "
            "if your customer may or may not be in your checkout flow. "
            "Stripe uses `setup_future_usage` to dynamically optimize "
            "your payment flow and comply with regional legislation and network rules. "
            "For example, if your customer is impacted by SCA, using `off_session` "
            "will ensure that they are authenticated while processing this "
            "PaymentIntent. You will then be able to make later off-session payments "
            "for this customer."
        ),
    )
    shipping = JSONField(
        null=True, blank=True, help_text="Shipping information for this PaymentIntent."
    )
    statement_descriptor = models.CharField(
        max_length=22,
        blank=True,
        help_text=(
            "For non-card charges, you can use this value as the complete description "
            "that appears on your customers' statements. Must contain at least one "
            "letter, maximum 22 characters."
        ),
    )
    status = StripeEnumField(
        enum=enums.PaymentIntentStatus,
        help_text=(
            "Status of this PaymentIntent, one of requires_payment_method, "
            "requires_confirmation, requires_action, processing, requires_capture, "
            "canceled, or succeeded. "
            "You can read more about PaymentIntent statuses here."
        ),
    )
    transfer_data = JSONField(
        null=True,
        blank=True,
        help_text=(
            "The data with which to automatically create a Transfer when the payment "
            "is finalized. "
            "See the PaymentIntents Connect usage guide for details."
        ),
    )
    transfer_group = models.CharField(
        blank=True,
        max_length=255,
        help_text=(
            "A string that identifies the resulting payment as part of a group. "
            "See the PaymentIntents Connect usage guide for details."
        ),
    )

    def __str__(self):
        account = self.on_behalf_of
        customer = self.customer
        amount = get_friendly_currency_amount(self.amount / 100, self.currency)
        status = enums.PaymentIntentStatus.humanize(self.status)

        if account and customer:
            return f"{amount} ({status}) for {account} by {customer}"
        if account:
            return f"{amount} for {account}. {status}"
        if customer:
            return f"{amount} by {customer}. {status}"

        return f"{amount} ({status})"

    def update(self, api_key=None, **kwargs):
        """
        Call the stripe API's modify operation for this model

        :param api_key: The api key to use for this request.
            Defaults to djstripe_settings.STRIPE_SECRET_KEY.
        :type api_key: string
        """
        api_key = api_key or self.default_api_key
        response = self.api_retrieve(api_key=api_key)
        return response.modify(response.stripe_id, api_key=api_key, **kwargs)

    def _api_cancel(self, api_key=None, **kwargs):
        """
        Call the stripe API's cancel operation for this model

        :param api_key: The api key to use for this request.
            Defaults to djstripe_settings.STRIPE_SECRET_KEY.
        :type api_key: string
        """
        api_key = api_key or self.default_api_key
        return self.api_retrieve(api_key=api_key).cancel(**kwargs)

    def _api_confirm(self, api_key=None, **kwargs):
        """
        Call the stripe API's confirm operation for this model.

        Confirm that your customer intends to pay with current or
        provided payment method. Upon confirmation, the PaymentIntent
        will attempt to initiate a payment.

        :param api_key: The api key to use for this request.
            Defaults to djstripe_settings.STRIPE_SECRET_KEY.
        :type api_key: string
        """
        api_key = api_key or self.default_api_key
        return self.api_retrieve(api_key=api_key).confirm(**kwargs)

Attributes

djstripe.models.core.PaymentIntent.amount = StripeQuantumCurrencyAmountField(help_text='Amount (in cents) intended to be collected by this PaymentIntent.') class-attribute instance-attribute
djstripe.models.core.PaymentIntent.amount_capturable = StripeQuantumCurrencyAmountField(help_text='Amount (in cents) that can be captured from this PaymentIntent.') class-attribute instance-attribute
djstripe.models.core.PaymentIntent.amount_received = StripeQuantumCurrencyAmountField(help_text='Amount (in cents) that was collected by this PaymentIntent.') class-attribute instance-attribute
djstripe.models.core.PaymentIntent.canceled_at = StripeDateTimeField(null=True, blank=True, default=None, help_text='Populated when status is canceled, this is the time at which the PaymentIntent was canceled. Measured in seconds since the Unix epoch.') class-attribute instance-attribute
djstripe.models.core.PaymentIntent.cancellation_reason = StripeEnumField(enum=enums.PaymentIntentCancellationReason, blank=True, help_text='Reason for cancellation of this PaymentIntent, either user-provided (duplicate, fraudulent, requested_by_customer, or abandoned) or generated by Stripe internally (failed_invoice, void_invoice, or automatic).') class-attribute instance-attribute
djstripe.models.core.PaymentIntent.capture_method = StripeEnumField(enum=enums.CaptureMethod, help_text='Capture method of this PaymentIntent, one of automatic or manual.') class-attribute instance-attribute
djstripe.models.core.PaymentIntent.client_secret = models.TextField(max_length=5000, help_text='The client secret of this PaymentIntent. Used for client-side retrieval using a publishable key.') class-attribute instance-attribute
djstripe.models.core.PaymentIntent.confirmation_method = StripeEnumField(enum=enums.ConfirmationMethod, help_text='Confirmation method of this PaymentIntent, one of manual or automatic.') class-attribute instance-attribute
djstripe.models.core.PaymentIntent.currency = StripeCurrencyCodeField() class-attribute instance-attribute
djstripe.models.core.PaymentIntent.customer = StripeForeignKey('Customer', null=True, on_delete=models.CASCADE, help_text='Customer this PaymentIntent is for if one exists.') class-attribute instance-attribute
djstripe.models.core.PaymentIntent.description = models.TextField(max_length=1000, default='', blank=True, help_text='An arbitrary string attached to the object. Often useful for displaying to users.') class-attribute instance-attribute
djstripe.models.core.PaymentIntent.last_payment_error = JSONField(null=True, blank=True, help_text='The payment error encountered in the previous PaymentIntent confirmation.') class-attribute instance-attribute
djstripe.models.core.PaymentIntent.next_action = JSONField(null=True, blank=True, help_text='If present, this property tells you what actions you need to take in order for your customer to fulfill a payment using the provided source.') class-attribute instance-attribute
djstripe.models.core.PaymentIntent.on_behalf_of = StripeForeignKey('Account', on_delete=models.CASCADE, null=True, blank=True, help_text='The account (if any) for which the funds of the PaymentIntent are intended.', related_name='payment_intents') class-attribute instance-attribute
djstripe.models.core.PaymentIntent.payment_method = StripeForeignKey('PaymentMethod', on_delete=models.SET_NULL, null=True, blank=True, help_text='Payment method used in this PaymentIntent.') class-attribute instance-attribute
djstripe.models.core.PaymentIntent.payment_method_types = JSONField(help_text='The list of payment method types (e.g. card) that this PaymentIntent is allowed to use.') class-attribute instance-attribute
djstripe.models.core.PaymentIntent.receipt_email = models.CharField(blank=True, max_length=255, help_text='Email address that the receipt for the resulting payment will be sent to.') class-attribute instance-attribute
djstripe.models.core.PaymentIntent.setup_future_usage = StripeEnumField(enum=enums.IntentUsage, null=True, blank=True, help_text="Indicates that you intend to make future payments with this PaymentIntent's payment method. If present, the payment method used with this PaymentIntent can be attached to a Customer, even after the transaction completes. Use `on_session` if you intend to only reuse the payment method when your customer is present in your checkout flow. Use `off_session` if your customer may or may not be in your checkout flow. Stripe uses `setup_future_usage` to dynamically optimize your payment flow and comply with regional legislation and network rules. For example, if your customer is impacted by SCA, using `off_session` will ensure that they are authenticated while processing this PaymentIntent. You will then be able to make later off-session payments for this customer.") class-attribute instance-attribute
djstripe.models.core.PaymentIntent.shipping = JSONField(null=True, blank=True, help_text='Shipping information for this PaymentIntent.') class-attribute instance-attribute
djstripe.models.core.PaymentIntent.statement_descriptor = models.CharField(max_length=22, blank=True, help_text="For non-card charges, you can use this value as the complete description that appears on your customers' statements. Must contain at least one letter, maximum 22 characters.") class-attribute instance-attribute
djstripe.models.core.PaymentIntent.status = StripeEnumField(enum=enums.PaymentIntentStatus, help_text='Status of this PaymentIntent, one of requires_payment_method, requires_confirmation, requires_action, processing, requires_capture, canceled, or succeeded. You can read more about PaymentIntent statuses here.') class-attribute instance-attribute
djstripe.models.core.PaymentIntent.stripe_class = stripe.PaymentIntent class-attribute instance-attribute
djstripe.models.core.PaymentIntent.stripe_dashboard_item_name = 'payments' class-attribute instance-attribute
djstripe.models.core.PaymentIntent.transfer_data = JSONField(null=True, blank=True, help_text='The data with which to automatically create a Transfer when the payment is finalized. See the PaymentIntents Connect usage guide for details.') class-attribute instance-attribute
djstripe.models.core.PaymentIntent.transfer_group = models.CharField(blank=True, max_length=255, help_text='A string that identifies the resulting payment as part of a group. See the PaymentIntents Connect usage guide for details.') class-attribute instance-attribute

Functions

djstripe.models.core.PaymentIntent.__str__()
Source code in djstripe/models/core.py
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
def __str__(self):
    account = self.on_behalf_of
    customer = self.customer
    amount = get_friendly_currency_amount(self.amount / 100, self.currency)
    status = enums.PaymentIntentStatus.humanize(self.status)

    if account and customer:
        return f"{amount} ({status}) for {account} by {customer}"
    if account:
        return f"{amount} for {account}. {status}"
    if customer:
        return f"{amount} by {customer}. {status}"

    return f"{amount} ({status})"
djstripe.models.core.PaymentIntent.update(api_key=None, **kwargs)

Call the stripe API's modify operation for this model

:param api_key: The api key to use for this request. Defaults to djstripe_settings.STRIPE_SECRET_KEY. :type api_key: string

Source code in djstripe/models/core.py
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
def update(self, api_key=None, **kwargs):
    """
    Call the stripe API's modify operation for this model

    :param api_key: The api key to use for this request.
        Defaults to djstripe_settings.STRIPE_SECRET_KEY.
    :type api_key: string
    """
    api_key = api_key or self.default_api_key
    response = self.api_retrieve(api_key=api_key)
    return response.modify(response.stripe_id, api_key=api_key, **kwargs)

djstripe.models.core.Payout

Bases: StripeModel

A Payout object is created when you receive funds from Stripe, or when you initiate a payout to either a bank account or debit card of a connected Stripe account.

Stripe documentation: https://stripe.com/docs/api?lang=python#payouts

Source code in djstripe/models/core.py
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
class Payout(StripeModel):
    """
    A Payout object is created when you receive funds from Stripe, or when you initiate
    a payout to either a bank account or debit card of a connected Stripe account.

    Stripe documentation: https://stripe.com/docs/api?lang=python#payouts
    """

    expand_fields = ["destination"]
    stripe_class = stripe.Payout
    stripe_dashboard_item_name = "payouts"

    amount = StripeDecimalCurrencyAmountField(
        help_text=(
            "Amount (as decimal) to be transferred to your bank account or debit card."
        )
    )
    arrival_date = StripeDateTimeField(
        help_text=(
            "Date the payout is expected to arrive in the bank. "
            "This factors in delays like weekends or bank holidays."
        )
    )
    automatic = models.BooleanField(
        help_text=(
            "`true` if the payout was created by an automated payout schedule, "
            "and `false` if it was requested manually."
        )
    )
    balance_transaction = StripeForeignKey(
        "BalanceTransaction",
        on_delete=models.SET_NULL,
        null=True,
        help_text=(
            "Balance transaction that describes the impact on your account balance."
        ),
    )
    currency = StripeCurrencyCodeField()
    destination = StripeForeignKey(
        "BankAccount",
        on_delete=models.PROTECT,
        null=True,
        help_text="Bank account or card the payout was sent to.",
    )
    failure_balance_transaction = StripeForeignKey(
        "BalanceTransaction",
        on_delete=models.SET_NULL,
        related_name="failure_payouts",
        null=True,
        blank=True,
        help_text=(
            "If the payout failed or was canceled, this will be the balance "
            "transaction that reversed the initial balance transaction, and "
            "puts the funds from the failed payout back in your balance."
        ),
    )
    failure_code = StripeEnumField(
        enum=enums.PayoutFailureCode,
        default="",
        blank=True,
        help_text=(
            "Error code explaining reason for transfer failure if available. "
            "See https://stripe.com/docs/api?lang=python#transfer_failures."
        ),
    )
    failure_message = models.TextField(
        default="",
        blank=True,
        help_text=(
            "Message to user further explaining reason for payout failure if available."
        ),
    )
    method = StripeEnumField(
        max_length=8,
        enum=enums.PayoutMethod,
        help_text=(
            "The method used to send this payout. "
            "`instant` is only supported for payouts to debit cards."
        ),
    )
    original_payout = models.OneToOneField(
        "Payout",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        help_text=(
            "If this payout reverses another, this is the ID of the original payout."
        ),
    )
    reversed_by = models.OneToOneField(
        "Payout",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        help_text=(
            "If this payout was reversed, this is the ID of the payout that reverses"
            " this payout."
        ),
        related_name="reversed_payout",
    )
    source_type = StripeEnumField(
        enum=enums.PayoutSourceType,
        help_text="The source balance this payout came from.",
    )
    statement_descriptor = models.CharField(
        max_length=255,
        default="",
        blank=True,
        help_text=(
            "Extra information about a payout to be displayed "
            "on the user's bank statement."
        ),
    )
    status = StripeEnumField(
        enum=enums.PayoutStatus,
        help_text=(
            "Current status of the payout. "
            "A payout will be `pending` until it is submitted to the bank, "
            "at which point it becomes `in_transit`. "
            "It will then change to paid if the transaction goes through. "
            "If it does not go through successfully, "
            "its status will change to `failed` or `canceled`."
        ),
    )
    type = StripeEnumField(enum=enums.PayoutType)

    def __str__(self):
        return f"{self.amount} ({enums.PayoutStatus.humanize(self.status)})"

Attributes

djstripe.models.core.Payout.amount = StripeDecimalCurrencyAmountField(help_text='Amount (as decimal) to be transferred to your bank account or debit card.') class-attribute instance-attribute
djstripe.models.core.Payout.arrival_date = StripeDateTimeField(help_text='Date the payout is expected to arrive in the bank. This factors in delays like weekends or bank holidays.') class-attribute instance-attribute
djstripe.models.core.Payout.automatic = models.BooleanField(help_text='`true` if the payout was created by an automated payout schedule, and `false` if it was requested manually.') class-attribute instance-attribute
djstripe.models.core.Payout.balance_transaction = StripeForeignKey('BalanceTransaction', on_delete=models.SET_NULL, null=True, help_text='Balance transaction that describes the impact on your account balance.') class-attribute instance-attribute
djstripe.models.core.Payout.currency = StripeCurrencyCodeField() class-attribute instance-attribute
djstripe.models.core.Payout.destination = StripeForeignKey('BankAccount', on_delete=models.PROTECT, null=True, help_text='Bank account or card the payout was sent to.') class-attribute instance-attribute
djstripe.models.core.Payout.expand_fields = ['destination'] class-attribute instance-attribute
djstripe.models.core.Payout.failure_balance_transaction = StripeForeignKey('BalanceTransaction', on_delete=models.SET_NULL, related_name='failure_payouts', null=True, blank=True, help_text='If the payout failed or was canceled, this will be the balance transaction that reversed the initial balance transaction, and puts the funds from the failed payout back in your balance.') class-attribute instance-attribute
djstripe.models.core.Payout.failure_code = StripeEnumField(enum=enums.PayoutFailureCode, default='', blank=True, help_text='Error code explaining reason for transfer failure if available. See https://stripe.com/docs/api?lang=python#transfer_failures.') class-attribute instance-attribute
djstripe.models.core.Payout.failure_message = models.TextField(default='', blank=True, help_text='Message to user further explaining reason for payout failure if available.') class-attribute instance-attribute
djstripe.models.core.Payout.method = StripeEnumField(max_length=8, enum=enums.PayoutMethod, help_text='The method used to send this payout. `instant` is only supported for payouts to debit cards.') class-attribute instance-attribute
djstripe.models.core.Payout.original_payout = models.OneToOneField('Payout', on_delete=models.SET_NULL, null=True, blank=True, help_text='If this payout reverses another, this is the ID of the original payout.') class-attribute instance-attribute
djstripe.models.core.Payout.reversed_by = models.OneToOneField('Payout', on_delete=models.SET_NULL, null=True, blank=True, help_text='If this payout was reversed, this is the ID of the payout that reverses this payout.', related_name='reversed_payout') class-attribute instance-attribute
djstripe.models.core.Payout.source_type = StripeEnumField(enum=enums.PayoutSourceType, help_text='The source balance this payout came from.') class-attribute instance-attribute
djstripe.models.core.Payout.statement_descriptor = models.CharField(max_length=255, default='', blank=True, help_text="Extra information about a payout to be displayed on the user's bank statement.") class-attribute instance-attribute
djstripe.models.core.Payout.status = StripeEnumField(enum=enums.PayoutStatus, help_text='Current status of the payout. A payout will be `pending` until it is submitted to the bank, at which point it becomes `in_transit`. It will then change to paid if the transaction goes through. If it does not go through successfully, its status will change to `failed` or `canceled`.') class-attribute instance-attribute
djstripe.models.core.Payout.stripe_class = stripe.Payout class-attribute instance-attribute
djstripe.models.core.Payout.stripe_dashboard_item_name = 'payouts' class-attribute instance-attribute
djstripe.models.core.Payout.type = StripeEnumField(enum=enums.PayoutType) class-attribute instance-attribute

Functions

djstripe.models.core.Payout.__str__()
Source code in djstripe/models/core.py
2253
2254
def __str__(self):
    return f"{self.amount} ({enums.PayoutStatus.humanize(self.status)})"

djstripe.models.core.Price

Bases: StripeModel

Prices define the unit cost, currency, and (optional) billing cycle for both recurring and one-time purchases of products.

Price and Plan objects are the same, but use a different representation. Creating a recurring Price in Stripe also makes a Plan available, and vice versa. This is not the case for a Price with interval=one_time.

Price objects are a more recent API representation, support more features and its usage is encouraged instead of Plan objects.

Stripe documentation: - https://stripe.com/docs/api/prices - https://stripe.com/docs/billing/prices-guide

Source code in djstripe/models/core.py
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
class Price(StripeModel):
    """
    Prices define the unit cost, currency, and (optional) billing cycle for
    both recurring and one-time purchases of products.

    Price and Plan objects are the same, but use a different representation.
    Creating a recurring Price in Stripe also makes a Plan available, and vice versa.
    This is not the case for a Price with interval=one_time.

    Price objects are a more recent API representation, support more features
    and its usage is encouraged instead of Plan objects.

    Stripe documentation:
    - https://stripe.com/docs/api/prices
    - https://stripe.com/docs/billing/prices-guide
    """

    stripe_class = stripe.Price
    expand_fields = ["product", "tiers"]
    stripe_dashboard_item_name = "prices"

    active = models.BooleanField(
        help_text="Whether the price can be used for new purchases."
    )
    currency = StripeCurrencyCodeField()
    nickname = models.CharField(
        max_length=250,
        blank=True,
        help_text="A brief description of the plan, hidden from customers.",
    )
    product = StripeForeignKey(
        "Product",
        on_delete=models.CASCADE,
        related_name="prices",
        help_text="The product this price is associated with.",
    )
    recurring = JSONField(
        default=None,
        blank=True,
        null=True,
        help_text=(
            "The recurring components of a price such as `interval` and `usage_type`."
        ),
    )
    type = StripeEnumField(
        enum=enums.PriceType,
        help_text=(
            "Whether the price is for a one-time purchase or a recurring "
            "(subscription) purchase."
        ),
    )
    unit_amount = StripeQuantumCurrencyAmountField(
        null=True,
        blank=True,
        help_text=(
            "The unit amount in cents to be charged, represented as a whole "
            "integer if possible. Null if a sub-cent precision is required."
        ),
    )
    unit_amount_decimal = StripeDecimalCurrencyAmountField(
        null=True,
        blank=True,
        max_digits=19,
        decimal_places=12,
        help_text=(
            "The unit amount in cents to be charged, represented as a decimal "
            "string with at most 12 decimal places."
        ),
    )

    # More attributes…
    billing_scheme = StripeEnumField(
        enum=enums.BillingScheme,
        blank=True,
        help_text=(
            "Describes how to compute the price per period. "
            "Either `per_unit` or `tiered`. "
            "`per_unit` indicates that the fixed amount (specified in `unit_amount` "
            "or `unit_amount_decimal`) will be charged per unit in `quantity` "
            "(for prices with `usage_type=licensed`), or per unit of total "
            "usage (for prices with `usage_type=metered`). "
            "`tiered` indicates that the unit pricing will be computed using "
            "a tiering strategy as defined using the `tiers` and `tiers_mode` "
            "attributes."
        ),
    )
    lookup_key = models.CharField(
        max_length=250,
        null=True,
        blank=True,
        help_text=(
            "A lookup key used to retrieve prices dynamically from a static string."
        ),
    )
    tiers = JSONField(
        null=True,
        blank=True,
        help_text=(
            "Each element represents a pricing tier. "
            "This parameter requires `billing_scheme` to be set to `tiered`."
        ),
    )
    tiers_mode = StripeEnumField(
        enum=enums.PriceTiersMode,
        null=True,
        blank=True,
        help_text=(
            "Defines if the tiering price should be `graduated` or `volume` based. "
            "In `volume`-based tiering, the maximum quantity within a period "
            "determines the per unit price, in `graduated` tiering pricing can "
            "successively change as the quantity grows."
        ),
    )
    transform_quantity = JSONField(
        null=True,
        blank=True,
        help_text=(
            "Apply a transformation to the reported usage or set quantity "
            "before computing the amount billed. Cannot be combined with `tiers`."
        ),
    )

    class Meta(object):
        ordering = ["unit_amount"]

    @classmethod
    def get_or_create(cls, **kwargs):
        """Get or create a Price."""

        try:
            return cls.objects.get(id=kwargs["id"]), False
        except cls.DoesNotExist:
            return cls.create(**kwargs), True

    @classmethod
    def create(cls, **kwargs):
        # A few minor things are changed in the api-version of the create call
        api_kwargs = dict(kwargs)
        if api_kwargs["unit_amount"]:
            api_kwargs["unit_amount"] = int(api_kwargs["unit_amount"] * 100)

        if isinstance(api_kwargs.get("product"), StripeModel):
            api_kwargs["product"] = api_kwargs["product"].id

        stripe_price = cls._api_create(**api_kwargs)

        api_key = api_kwargs.get("api_key") or djstripe_settings.STRIPE_SECRET_KEY
        price = cls.sync_from_stripe_data(stripe_price, api_key=api_key)

        return price

    def __str__(self):
        return f"{self.human_readable_price} for {self.product.name}"

    @property
    def human_readable_price(self):
        if self.billing_scheme == "per_unit":
            unit_amount = (self.unit_amount or 0) / 100
            amount = get_friendly_currency_amount(unit_amount, self.currency)
        else:
            # tiered billing scheme
            tier_1 = self.tiers[0]
            formatted_unit_amount_tier_1 = get_friendly_currency_amount(
                (tier_1["unit_amount"] or 0) / 100, self.currency
            )
            amount = f"Starts at {formatted_unit_amount_tier_1} per unit"

            # stripe shows flat fee even if it is set to 0.00
            flat_amount_tier_1 = tier_1["flat_amount"]
            if flat_amount_tier_1 is not None:
                formatted_flat_amount_tier_1 = get_friendly_currency_amount(
                    flat_amount_tier_1 / 100, self.currency
                )
                amount = f"{amount} + {formatted_flat_amount_tier_1}"

        format_args = {"amount": amount}

        if self.recurring:
            interval_count = self.recurring["interval_count"]
            if interval_count == 1:
                interval = {
                    "day": _("day"),
                    "week": _("week"),
                    "month": _("month"),
                    "year": _("year"),
                }[self.recurring["interval"]]
                template = _("{amount}/{interval}")
                format_args["interval"] = interval
            else:
                interval = {
                    "day": _("days"),
                    "week": _("weeks"),
                    "month": _("months"),
                    "year": _("years"),
                }[self.recurring["interval"]]
                template = _("{amount} / every {interval_count} {interval}")
                format_args["interval"] = interval
                format_args["interval_count"] = interval_count

        else:
            template = _("{amount} (one time)")

        return format_lazy(template, **format_args)

Attributes

djstripe.models.core.Price.active = models.BooleanField(help_text='Whether the price can be used for new purchases.') class-attribute instance-attribute
djstripe.models.core.Price.billing_scheme = StripeEnumField(enum=enums.BillingScheme, blank=True, help_text='Describes how to compute the price per period. Either `per_unit` or `tiered`. `per_unit` indicates that the fixed amount (specified in `unit_amount` or `unit_amount_decimal`) will be charged per unit in `quantity` (for prices with `usage_type=licensed`), or per unit of total usage (for prices with `usage_type=metered`). `tiered` indicates that the unit pricing will be computed using a tiering strategy as defined using the `tiers` and `tiers_mode` attributes.') class-attribute instance-attribute
djstripe.models.core.Price.currency = StripeCurrencyCodeField() class-attribute instance-attribute
djstripe.models.core.Price.expand_fields = ['product', 'tiers'] class-attribute instance-attribute
djstripe.models.core.Price.human_readable_price property
djstripe.models.core.Price.lookup_key = models.CharField(max_length=250, null=True, blank=True, help_text='A lookup key used to retrieve prices dynamically from a static string.') class-attribute instance-attribute
djstripe.models.core.Price.nickname = models.CharField(max_length=250, blank=True, help_text='A brief description of the plan, hidden from customers.') class-attribute instance-attribute
djstripe.models.core.Price.product = StripeForeignKey('Product', on_delete=models.CASCADE, related_name='prices', help_text='The product this price is associated with.') class-attribute instance-attribute
djstripe.models.core.Price.recurring = JSONField(default=None, blank=True, null=True, help_text='The recurring components of a price such as `interval` and `usage_type`.') class-attribute instance-attribute
djstripe.models.core.Price.stripe_class = stripe.Price class-attribute instance-attribute
djstripe.models.core.Price.stripe_dashboard_item_name = 'prices' class-attribute instance-attribute
djstripe.models.core.Price.tiers = JSONField(null=True, blank=True, help_text='Each element represents a pricing tier. This parameter requires `billing_scheme` to be set to `tiered`.') class-attribute instance-attribute
djstripe.models.core.Price.tiers_mode = StripeEnumField(enum=enums.PriceTiersMode, null=True, blank=True, help_text='Defines if the tiering price should be `graduated` or `volume` based. In `volume`-based tiering, the maximum quantity within a period determines the per unit price, in `graduated` tiering pricing can successively change as the quantity grows.') class-attribute instance-attribute
djstripe.models.core.Price.transform_quantity = JSONField(null=True, blank=True, help_text='Apply a transformation to the reported usage or set quantity before computing the amount billed. Cannot be combined with `tiers`.') class-attribute instance-attribute
djstripe.models.core.Price.type = StripeEnumField(enum=enums.PriceType, help_text='Whether the price is for a one-time purchase or a recurring (subscription) purchase.') class-attribute instance-attribute
djstripe.models.core.Price.unit_amount = StripeQuantumCurrencyAmountField(null=True, blank=True, help_text='The unit amount in cents to be charged, represented as a whole integer if possible. Null if a sub-cent precision is required.') class-attribute instance-attribute
djstripe.models.core.Price.unit_amount_decimal = StripeDecimalCurrencyAmountField(null=True, blank=True, max_digits=19, decimal_places=12, help_text='The unit amount in cents to be charged, represented as a decimal string with at most 12 decimal places.') class-attribute instance-attribute

Classes

djstripe.models.core.Price.Meta

Bases: object

Source code in djstripe/models/core.py
2379
2380
class Meta(object):
    ordering = ["unit_amount"]
Attributes
djstripe.models.core.Price.Meta.ordering = ['unit_amount'] class-attribute instance-attribute

Functions

djstripe.models.core.Price.__str__()
Source code in djstripe/models/core.py
2408
2409
def __str__(self):
    return f"{self.human_readable_price} for {self.product.name}"
djstripe.models.core.Price.create(**kwargs) classmethod
Source code in djstripe/models/core.py
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
@classmethod
def create(cls, **kwargs):
    # A few minor things are changed in the api-version of the create call
    api_kwargs = dict(kwargs)
    if api_kwargs["unit_amount"]:
        api_kwargs["unit_amount"] = int(api_kwargs["unit_amount"] * 100)

    if isinstance(api_kwargs.get("product"), StripeModel):
        api_kwargs["product"] = api_kwargs["product"].id

    stripe_price = cls._api_create(**api_kwargs)

    api_key = api_kwargs.get("api_key") or djstripe_settings.STRIPE_SECRET_KEY
    price = cls.sync_from_stripe_data(stripe_price, api_key=api_key)

    return price
djstripe.models.core.Price.get_or_create(**kwargs) classmethod

Get or create a Price.

Source code in djstripe/models/core.py
2382
2383
2384
2385
2386
2387
2388
2389
@classmethod
def get_or_create(cls, **kwargs):
    """Get or create a Price."""

    try:
        return cls.objects.get(id=kwargs["id"]), False
    except cls.DoesNotExist:
        return cls.create(**kwargs), True

djstripe.models.core.Product

Bases: StripeModel

Products describe the specific goods or services you offer to your customers. For example, you might offer a Standard and Premium version of your goods or service; each version would be a separate Product. They can be used in conjunction with Prices to configure pricing in Payment Links, Checkout, and Subscriptions.

Stripe documentation: https://stripe.com/docs/api?lang=python#products

Source code in djstripe/models/core.py
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
class Product(StripeModel):
    """
    Products describe the specific goods or services you offer to your customers.
    For example, you might offer a Standard and Premium version of your goods or service;
    each version would be a separate Product. They can be used in conjunction with
    Prices to configure pricing in Payment Links, Checkout, and Subscriptions.

    Stripe documentation: https://stripe.com/docs/api?lang=python#products
    """

    stripe_class = stripe.Product
    stripe_dashboard_item_name = "products"

    # Fields applicable to both `good` and `service`
    name = models.TextField(
        max_length=5000,
        help_text=(
            "The product's name, meant to be displayable to the customer. "
            "Applicable to both `service` and `good` types."
        ),
    )
    default_price = StripeForeignKey(
        "Price",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="products",
        help_text="The default price this product is associated with.",
    )
    type = StripeEnumField(
        enum=enums.ProductType,
        help_text=(
            "The type of the product. The product is either of type `good`, which is "
            "eligible for use with Orders and SKUs, or `service`, which is eligible "
            "for use with Subscriptions and Plans."
        ),
    )

    # Fields applicable to `good` only
    active = models.BooleanField(
        null=True,
        help_text=(
            "Whether the product is currently available for purchase. "
            "Only applicable to products of `type=good`."
        ),
    )
    attributes = JSONField(
        null=True,
        blank=True,
        help_text=(
            "A list of up to 5 attributes that each SKU can provide values for "
            '(e.g., `["color", "size"]`). Only applicable to products of `type=good`.'
        ),
    )
    caption = models.TextField(
        default="",
        blank=True,
        max_length=5000,
        help_text=(
            "A short one-line description of the product, meant to be displayable"
            "to the customer. Only applicable to products of `type=good`."
        ),
    )
    deactivate_on = JSONField(
        null=True,
        blank=True,
        help_text=(
            "An array of connect application identifiers that cannot purchase "
            "this product. Only applicable to products of `type=good`."
        ),
    )
    images = JSONField(
        null=True,
        blank=True,
        help_text=(
            "A list of up to 8 URLs of images for this product, meant to be "
            "displayable to the customer. Only applicable to products of `type=good`."
        ),
    )
    package_dimensions = JSONField(
        null=True,
        blank=True,
        help_text=(
            "The dimensions of this product for shipping purposes. "
            "A SKU associated with this product can override this value by having its "
            "own `package_dimensions`. Only applicable to products of `type=good`."
        ),
    )
    shippable = models.BooleanField(
        null=True,
        blank=True,
        help_text=(
            "Whether this product is a shipped good. "
            "Only applicable to products of `type=good`."
        ),
    )
    url = models.CharField(
        max_length=799,
        null=True,
        blank=True,
        help_text=(
            "A URL of a publicly-accessible webpage for this product. "
            "Only applicable to products of `type=good`."
        ),
    )

    # Fields available to `service` only
    statement_descriptor = models.CharField(
        max_length=22,
        default="",
        blank=True,
        help_text=(
            "Extra information about a product which will appear on your customer's "
            "credit card statement. In the case that multiple products are billed at "
            "once, the first statement descriptor will be used. "
            "Only available on products of type=`service`."
        ),
    )
    unit_label = models.CharField(max_length=12, default="", blank=True)

    def __str__(self):
        # 1 product can have 1 or more than 1 related price
        price_qs = self.prices.all()
        price_count = price_qs.count()

        if price_count > 1:
            return f"{self.name} ({price_count} prices)"
        elif price_count == 1:
            return f"{self.name} ({price_qs[0].human_readable_price})"
        else:
            return self.name

Attributes

djstripe.models.core.Product.active = models.BooleanField(null=True, help_text='Whether the product is currently available for purchase. Only applicable to products of `type=good`.') class-attribute instance-attribute
djstripe.models.core.Product.attributes = JSONField(null=True, blank=True, help_text='A list of up to 5 attributes that each SKU can provide values for (e.g., `["color", "size"]`). Only applicable to products of `type=good`.') class-attribute instance-attribute
djstripe.models.core.Product.caption = models.TextField(default='', blank=True, max_length=5000, help_text='A short one-line description of the product, meant to be displayableto the customer. Only applicable to products of `type=good`.') class-attribute instance-attribute
djstripe.models.core.Product.deactivate_on = JSONField(null=True, blank=True, help_text='An array of connect application identifiers that cannot purchase this product. Only applicable to products of `type=good`.') class-attribute instance-attribute
djstripe.models.core.Product.default_price = StripeForeignKey('Price', on_delete=models.SET_NULL, null=True, blank=True, related_name='products', help_text='The default price this product is associated with.') class-attribute instance-attribute
djstripe.models.core.Product.images = JSONField(null=True, blank=True, help_text='A list of up to 8 URLs of images for this product, meant to be displayable to the customer. Only applicable to products of `type=good`.') class-attribute instance-attribute
djstripe.models.core.Product.name = models.TextField(max_length=5000, help_text="The product's name, meant to be displayable to the customer. Applicable to both `service` and `good` types.") class-attribute instance-attribute
djstripe.models.core.Product.package_dimensions = JSONField(null=True, blank=True, help_text='The dimensions of this product for shipping purposes. A SKU associated with this product can override this value by having its own `package_dimensions`. Only applicable to products of `type=good`.') class-attribute instance-attribute
djstripe.models.core.Product.shippable = models.BooleanField(null=True, blank=True, help_text='Whether this product is a shipped good. Only applicable to products of `type=good`.') class-attribute instance-attribute
djstripe.models.core.Product.statement_descriptor = models.CharField(max_length=22, default='', blank=True, help_text="Extra information about a product which will appear on your customer's credit card statement. In the case that multiple products are billed at once, the first statement descriptor will be used. Only available on products of type=`service`.") class-attribute instance-attribute
djstripe.models.core.Product.stripe_class = stripe.Product class-attribute instance-attribute
djstripe.models.core.Product.stripe_dashboard_item_name = 'products' class-attribute instance-attribute
djstripe.models.core.Product.type = StripeEnumField(enum=enums.ProductType, help_text='The type of the product. The product is either of type `good`, which is eligible for use with Orders and SKUs, or `service`, which is eligible for use with Subscriptions and Plans.') class-attribute instance-attribute
djstripe.models.core.Product.unit_label = models.CharField(max_length=12, default='', blank=True) class-attribute instance-attribute
djstripe.models.core.Product.url = models.CharField(max_length=799, null=True, blank=True, help_text='A URL of a publicly-accessible webpage for this product. Only applicable to products of `type=good`.') class-attribute instance-attribute

Functions

djstripe.models.core.Product.__str__()
Source code in djstripe/models/core.py
665
666
667
668
669
670
671
672
673
674
675
def __str__(self):
    # 1 product can have 1 or more than 1 related price
    price_qs = self.prices.all()
    price_count = price_qs.count()

    if price_count > 1:
        return f"{self.name} ({price_count} prices)"
    elif price_count == 1:
        return f"{self.name} ({price_qs[0].human_readable_price})"
    else:
        return self.name

djstripe.models.core.Refund

Bases: StripeModel

Refund objects allow you to refund a charge that has previously been created but not yet refunded. Funds will be refunded to the credit or debit card that was originally charged.

Stripe documentation: https://stripe.com/docs/api?lang=python#refund_object

Source code in djstripe/models/core.py
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
class Refund(StripeModel):
    """
    Refund objects allow you to refund a charge that has previously been created
    but not yet refunded. Funds will be refunded to the credit or debit card
    that was originally charged.

    Stripe documentation: https://stripe.com/docs/api?lang=python#refund_object
    """

    stripe_class = stripe.Refund

    amount = StripeQuantumCurrencyAmountField(help_text="Amount, in cents.")
    balance_transaction = StripeForeignKey(
        "BalanceTransaction",
        on_delete=models.SET_NULL,
        null=True,
        help_text=(
            "Balance transaction that describes the impact on your account balance."
        ),
    )
    charge = StripeForeignKey(
        "Charge",
        on_delete=models.CASCADE,
        related_name="refunds",
        help_text="The charge that was refunded",
    )
    currency = StripeCurrencyCodeField()
    failure_balance_transaction = StripeForeignKey(
        "BalanceTransaction",
        on_delete=models.SET_NULL,
        related_name="failure_refunds",
        null=True,
        blank=True,
        help_text=(
            "If the refund failed, this balance transaction describes the "
            "adjustment made on your account balance that reverses the initial "
            "balance transaction."
        ),
    )
    failure_reason = StripeEnumField(
        enum=enums.RefundFailureReason,
        default="",
        blank=True,
        help_text="If the refund failed, the reason for refund failure if known.",
    )
    reason = StripeEnumField(
        enum=enums.RefundReason,
        blank=True,
        default="",
        help_text="Reason for the refund.",
    )
    receipt_number = models.CharField(
        max_length=9,
        default="",
        blank=True,
        help_text=(
            "The transaction number that appears on email receipts sent "
            "for this charge."
        ),
    )
    status = StripeEnumField(
        blank=True, enum=enums.RefundStatus, help_text="Status of the refund."
    )
    # todo implement source_transfer_reversal and transfer_reversal

    def get_stripe_dashboard_url(self):
        return self.charge.get_stripe_dashboard_url()

    def __str__(self):
        amount = get_friendly_currency_amount(self.amount / 100, self.currency)
        status = enums.RefundStatus.humanize(self.status)
        return f"{amount} ({status})"

Attributes

djstripe.models.core.Refund.amount = StripeQuantumCurrencyAmountField(help_text='Amount, in cents.') class-attribute instance-attribute
djstripe.models.core.Refund.balance_transaction = StripeForeignKey('BalanceTransaction', on_delete=models.SET_NULL, null=True, help_text='Balance transaction that describes the impact on your account balance.') class-attribute instance-attribute
djstripe.models.core.Refund.charge = StripeForeignKey('Charge', on_delete=models.CASCADE, related_name='refunds', help_text='The charge that was refunded') class-attribute instance-attribute
djstripe.models.core.Refund.currency = StripeCurrencyCodeField() class-attribute instance-attribute
djstripe.models.core.Refund.failure_balance_transaction = StripeForeignKey('BalanceTransaction', on_delete=models.SET_NULL, related_name='failure_refunds', null=True, blank=True, help_text='If the refund failed, this balance transaction describes the adjustment made on your account balance that reverses the initial balance transaction.') class-attribute instance-attribute
djstripe.models.core.Refund.failure_reason = StripeEnumField(enum=enums.RefundFailureReason, default='', blank=True, help_text='If the refund failed, the reason for refund failure if known.') class-attribute instance-attribute
djstripe.models.core.Refund.reason = StripeEnumField(enum=enums.RefundReason, blank=True, default='', help_text='Reason for the refund.') class-attribute instance-attribute
djstripe.models.core.Refund.receipt_number = models.CharField(max_length=9, default='', blank=True, help_text='The transaction number that appears on email receipts sent for this charge.') class-attribute instance-attribute
djstripe.models.core.Refund.status = StripeEnumField(blank=True, enum=enums.RefundStatus, help_text='Status of the refund.') class-attribute instance-attribute
djstripe.models.core.Refund.stripe_class = stripe.Refund class-attribute instance-attribute

Functions

djstripe.models.core.Refund.__str__()
Source code in djstripe/models/core.py
2530
2531
2532
2533
def __str__(self):
    amount = get_friendly_currency_amount(self.amount / 100, self.currency)
    status = enums.RefundStatus.humanize(self.status)
    return f"{amount} ({status})"
djstripe.models.core.Refund.get_stripe_dashboard_url()
Source code in djstripe/models/core.py
2527
2528
def get_stripe_dashboard_url(self):
    return self.charge.get_stripe_dashboard_url()

djstripe.models.core.SetupIntent

Bases: StripeModel

A SetupIntent guides you through the process of setting up a customer's payment credentials for future payments. For example, you could use a SetupIntent to set up your customer's card without immediately collecting a payment. Later, you can use PaymentIntents to drive the payment flow.

NOTE: You should not maintain long-lived, unconfirmed SetupIntents. For security purposes, SetupIntents older than 24 hours may no longer be valid.

Stripe documentation: https://stripe.com/docs/api?lang=python#setup_intents

Source code in djstripe/models/core.py
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
class SetupIntent(StripeModel):
    """
    A SetupIntent guides you through the process of setting up a customer's
    payment credentials for future payments. For example, you could use a SetupIntent
    to set up your customer's card without immediately collecting a payment.
    Later, you can use PaymentIntents to drive the payment flow.

    NOTE: You should not maintain long-lived, unconfirmed SetupIntents.
    For security purposes, SetupIntents older than 24 hours may no longer be valid.

    Stripe documentation: https://stripe.com/docs/api?lang=python#setup_intents
    """

    stripe_class = stripe.SetupIntent

    application = models.CharField(
        max_length=255,
        blank=True,
        help_text="ID of the Connect application that created the SetupIntent.",
    )
    cancellation_reason = StripeEnumField(
        enum=enums.SetupIntentCancellationReason,
        blank=True,
        help_text=(
            "Reason for cancellation of this SetupIntent, one of abandoned, "
            "requested_by_customer, or duplicate"
        ),
    )
    client_secret = models.TextField(
        max_length=5000,
        blank=True,
        help_text=(
            "The client secret of this SetupIntent. "
            "Used for client-side retrieval using a publishable key."
        ),
    )
    customer = StripeForeignKey(
        "Customer",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        help_text="Customer this SetupIntent belongs to, if one exists.",
    )
    last_setup_error = JSONField(
        null=True,
        blank=True,
        help_text="The error encountered in the previous SetupIntent confirmation.",
    )
    next_action = JSONField(
        null=True,
        blank=True,
        help_text=(
            "If present, this property tells you what actions you need to take in"
            "order for your customer to continue payment setup."
        ),
    )
    on_behalf_of = StripeForeignKey(
        "Account",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        help_text="The account (if any) for which the setup is intended.",
        related_name="setup_intents",
    )
    payment_method = StripeForeignKey(
        "PaymentMethod",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        help_text="Payment method used in this PaymentIntent.",
    )
    payment_method_types = JSONField(
        help_text=(
            "The list of payment method types (e.g. card) that this PaymentIntent is "
            "allowed to use."
        )
    )
    status = StripeEnumField(
        enum=enums.SetupIntentStatus,
        help_text=(
            "Status of this SetupIntent, one of requires_payment_method, "
            "requires_confirmation, requires_action, processing, "
            "canceled, or succeeded."
        ),
    )
    usage = StripeEnumField(
        enum=enums.IntentUsage,
        default=enums.IntentUsage.off_session,
        help_text=(
            "Indicates how the payment method is intended to be used in the future."
        ),
    )

    def __str__(self):
        account = self.on_behalf_of
        customer = self.customer

        if account and customer:
            return (
                f"{self.payment_method} ({enums.SetupIntentStatus.humanize(self.status)})"
                f" for {account} by {customer}"
            )

        if account:
            return (
                f"{self.payment_method} for {account}."
                f" {enums.SetupIntentStatus.humanize(self.status)}"
            )
        if customer:
            return (
                f"{self.payment_method} by {customer}."
                f" {enums.SetupIntentStatus.humanize(self.status)}"
            )
        return (
            f"{self.payment_method} ({enums.SetupIntentStatus.humanize(self.status)})"
        )

Attributes

djstripe.models.core.SetupIntent.application = models.CharField(max_length=255, blank=True, help_text='ID of the Connect application that created the SetupIntent.') class-attribute instance-attribute
djstripe.models.core.SetupIntent.cancellation_reason = StripeEnumField(enum=enums.SetupIntentCancellationReason, blank=True, help_text='Reason for cancellation of this SetupIntent, one of abandoned, requested_by_customer, or duplicate') class-attribute instance-attribute
djstripe.models.core.SetupIntent.client_secret = models.TextField(max_length=5000, blank=True, help_text='The client secret of this SetupIntent. Used for client-side retrieval using a publishable key.') class-attribute instance-attribute
djstripe.models.core.SetupIntent.customer = StripeForeignKey('Customer', null=True, blank=True, on_delete=models.SET_NULL, help_text='Customer this SetupIntent belongs to, if one exists.') class-attribute instance-attribute
djstripe.models.core.SetupIntent.last_setup_error = JSONField(null=True, blank=True, help_text='The error encountered in the previous SetupIntent confirmation.') class-attribute instance-attribute
djstripe.models.core.SetupIntent.next_action = JSONField(null=True, blank=True, help_text='If present, this property tells you what actions you need to take inorder for your customer to continue payment setup.') class-attribute instance-attribute
djstripe.models.core.SetupIntent.on_behalf_of = StripeForeignKey('Account', on_delete=models.SET_NULL, null=True, blank=True, help_text='The account (if any) for which the setup is intended.', related_name='setup_intents') class-attribute instance-attribute
djstripe.models.core.SetupIntent.payment_method = StripeForeignKey('PaymentMethod', on_delete=models.SET_NULL, null=True, blank=True, help_text='Payment method used in this PaymentIntent.') class-attribute instance-attribute
djstripe.models.core.SetupIntent.payment_method_types = JSONField(help_text='The list of payment method types (e.g. card) that this PaymentIntent is allowed to use.') class-attribute instance-attribute
djstripe.models.core.SetupIntent.status = StripeEnumField(enum=enums.SetupIntentStatus, help_text='Status of this SetupIntent, one of requires_payment_method, requires_confirmation, requires_action, processing, canceled, or succeeded.') class-attribute instance-attribute
djstripe.models.core.SetupIntent.stripe_class = stripe.SetupIntent class-attribute instance-attribute
djstripe.models.core.SetupIntent.usage = StripeEnumField(enum=enums.IntentUsage, default=enums.IntentUsage.off_session, help_text='Indicates how the payment method is intended to be used in the future.') class-attribute instance-attribute

Functions

djstripe.models.core.SetupIntent.__str__()
Source code in djstripe/models/core.py
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
def __str__(self):
    account = self.on_behalf_of
    customer = self.customer

    if account and customer:
        return (
            f"{self.payment_method} ({enums.SetupIntentStatus.humanize(self.status)})"
            f" for {account} by {customer}"
        )

    if account:
        return (
            f"{self.payment_method} for {account}."
            f" {enums.SetupIntentStatus.humanize(self.status)}"
        )
    if customer:
        return (
            f"{self.payment_method} by {customer}."
            f" {enums.SetupIntentStatus.humanize(self.status)}"
        )
    return (
        f"{self.payment_method} ({enums.SetupIntentStatus.humanize(self.status)})"
    )

Functions

Payment Methods

Attributes

Classes

djstripe.models.payment_methods.BankAccount

Bases: LegacySourceMixin, StripeModel

These bank accounts are payment methods on Customer objects. On the other hand External Accounts are transfer destinations on Account objects for Custom accounts. They can be bank accounts or debit cards as well.

Stripe documentation:https://stripe.com/docs/api/customer_bank_accounts

Source code in djstripe/models/payment_methods.py
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
class BankAccount(LegacySourceMixin, StripeModel):
    """
    These bank accounts are payment methods on Customer objects.
    On the other hand External Accounts are transfer destinations on Account
    objects for Custom accounts. They can be bank accounts or debit cards as well.

    Stripe documentation:https://stripe.com/docs/api/customer_bank_accounts
    """

    stripe_class = stripe.BankAccount

    account = StripeForeignKey(
        "Account",
        on_delete=models.PROTECT,
        null=True,
        blank=True,
        related_name="bank_accounts",
        help_text=(
            "The external account the charge was made on behalf of. Null here indicates"
            " that this value was never set."
        ),
    )
    account_holder_name = models.TextField(
        max_length=5000,
        blank=True,
        help_text="The name of the person or business that owns the bank account.",
    )
    account_holder_type = StripeEnumField(
        enum=enums.BankAccountHolderType,
        help_text="The type of entity that holds the account.",
    )
    bank_name = models.CharField(
        max_length=255,
        help_text=(
            "Name of the bank associated with the routing number (e.g., `WELLS FARGO`)."
        ),
    )
    country = models.CharField(
        max_length=2,
        help_text=(
            "Two-letter ISO code representing the country the bank account "
            "is located in."
        ),
    )
    currency = StripeCurrencyCodeField()
    customer = StripeForeignKey(
        "Customer", on_delete=models.SET_NULL, null=True, related_name="bank_account"
    )
    default_for_currency = models.BooleanField(
        null=True,
        help_text=(
            "Whether this external account (BankAccount) is the default account for "
            "its currency."
        ),
    )
    fingerprint = models.CharField(
        max_length=16,
        help_text=(
            "Uniquely identifies this particular bank account. "
            "You can use this attribute to check whether two bank accounts are "
            "the same."
        ),
    )
    last4 = models.CharField(max_length=4)
    routing_number = models.CharField(
        max_length=255, help_text="The routing transit number for the bank account."
    )
    status = StripeEnumField(enum=enums.BankAccountStatus)

    def __str__(self):
        default = False
        # prefer to show it by customer format if present
        if self.customer:
            default_source = self.customer.default_source
            default_payment_method = self.customer.default_payment_method

            if (default_payment_method and self.id == default_payment_method.id) or (
                default_source and self.id == default_source.id
            ):
                # current card is the default payment method or source
                default = True

            customer_template = (
                f"{self.bank_name} {self.routing_number} ({self.human_readable_status})"
                f" {'Default' if default else ''} {self.currency}"
            )
            return customer_template

        default = getattr(self, "default_for_currency", False)
        account_template = f"{self.bank_name} {self.currency} {'Default' if default else ''} {self.routing_number} {self.last4}"
        return account_template

    @property
    def human_readable_status(self):
        if self.status == "new":
            return "Pending Verification"
        return enums.BankAccountStatus.humanize(self.status)

    def api_retrieve(self, **kwargs):
        if not self.customer and not self.account:
            raise ImpossibleAPIRequest(
                "Can't retrieve a bank account without a customer or account object."
                " This may happen if not all accounts or customer objects are in the"
                ' db. Please run "python manage.py djstripe_sync_models Account'
                ' Customer" as a potential fix.'
            )

        return super().api_retrieve(**kwargs)

Attributes

djstripe.models.payment_methods.BankAccount.account = StripeForeignKey('Account', on_delete=models.PROTECT, null=True, blank=True, related_name='bank_accounts', help_text='The external account the charge was made on behalf of. Null here indicates that this value was never set.') class-attribute instance-attribute
djstripe.models.payment_methods.BankAccount.account_holder_name = models.TextField(max_length=5000, blank=True, help_text='The name of the person or business that owns the bank account.') class-attribute instance-attribute
djstripe.models.payment_methods.BankAccount.account_holder_type = StripeEnumField(enum=enums.BankAccountHolderType, help_text='The type of entity that holds the account.') class-attribute instance-attribute
djstripe.models.payment_methods.BankAccount.bank_name = models.CharField(max_length=255, help_text='Name of the bank associated with the routing number (e.g., `WELLS FARGO`).') class-attribute instance-attribute
djstripe.models.payment_methods.BankAccount.country = models.CharField(max_length=2, help_text='Two-letter ISO code representing the country the bank account is located in.') class-attribute instance-attribute
djstripe.models.payment_methods.BankAccount.currency = StripeCurrencyCodeField() class-attribute instance-attribute
djstripe.models.payment_methods.BankAccount.customer = StripeForeignKey('Customer', on_delete=models.SET_NULL, null=True, related_name='bank_account') class-attribute instance-attribute
djstripe.models.payment_methods.BankAccount.default_for_currency = models.BooleanField(null=True, help_text='Whether this external account (BankAccount) is the default account for its currency.') class-attribute instance-attribute
djstripe.models.payment_methods.BankAccount.fingerprint = models.CharField(max_length=16, help_text='Uniquely identifies this particular bank account. You can use this attribute to check whether two bank accounts are the same.') class-attribute instance-attribute
djstripe.models.payment_methods.BankAccount.human_readable_status property
djstripe.models.payment_methods.BankAccount.last4 = models.CharField(max_length=4) class-attribute instance-attribute
djstripe.models.payment_methods.BankAccount.routing_number = models.CharField(max_length=255, help_text='The routing transit number for the bank account.') class-attribute instance-attribute
djstripe.models.payment_methods.BankAccount.status = StripeEnumField(enum=enums.BankAccountStatus) class-attribute instance-attribute
djstripe.models.payment_methods.BankAccount.stripe_class = stripe.BankAccount class-attribute instance-attribute

Functions

djstripe.models.payment_methods.BankAccount.__str__()
Source code in djstripe/models/payment_methods.py
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
def __str__(self):
    default = False
    # prefer to show it by customer format if present
    if self.customer:
        default_source = self.customer.default_source
        default_payment_method = self.customer.default_payment_method

        if (default_payment_method and self.id == default_payment_method.id) or (
            default_source and self.id == default_source.id
        ):
            # current card is the default payment method or source
            default = True

        customer_template = (
            f"{self.bank_name} {self.routing_number} ({self.human_readable_status})"
            f" {'Default' if default else ''} {self.currency}"
        )
        return customer_template

    default = getattr(self, "default_for_currency", False)
    account_template = f"{self.bank_name} {self.currency} {'Default' if default else ''} {self.routing_number} {self.last4}"
    return account_template
djstripe.models.payment_methods.BankAccount.api_retrieve(**kwargs)
Source code in djstripe/models/payment_methods.py
468
469
470
471
472
473
474
475
476
477
def api_retrieve(self, **kwargs):
    if not self.customer and not self.account:
        raise ImpossibleAPIRequest(
            "Can't retrieve a bank account without a customer or account object."
            " This may happen if not all accounts or customer objects are in the"
            ' db. Please run "python manage.py djstripe_sync_models Account'
            ' Customer" as a potential fix.'
        )

    return super().api_retrieve(**kwargs)

djstripe.models.payment_methods.Card

Bases: LegacySourceMixin, StripeModel

You can store multiple cards on a customer in order to charge the customer later.

This is a legacy model which only applies to the "v2" Stripe API (eg. Checkout.js). You should strive to use the Stripe "v3" API (eg. Stripe Elements). Also see: https://stripe.com/docs/stripe-js/elements/migrating When using Elements, you will not be using Card objects. Instead, you will use Source objects. A Source object of type "card" is equivalent to a Card object. However, Card objects cannot be converted into Source objects by Stripe at this time.

Stripe documentation: https://stripe.com/docs/api?lang=python#cards

Source code in djstripe/models/payment_methods.py
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
class Card(LegacySourceMixin, StripeModel):
    """
    You can store multiple cards on a customer in order to charge the customer later.

    This is a legacy model which only applies to the "v2" Stripe API (eg. Checkout.js).
    You should strive to use the Stripe "v3" API (eg. Stripe Elements).
    Also see: https://stripe.com/docs/stripe-js/elements/migrating
    When using Elements, you will not be using Card objects. Instead, you will use
    Source objects.
    A Source object of type "card" is equivalent to a Card object. However, Card
    objects cannot be converted into Source objects by Stripe at this time.

    Stripe documentation: https://stripe.com/docs/api?lang=python#cards
    """

    stripe_class = stripe.Card
    # Stripe Custom Connected Accounts can have cards as "Payout Sources"
    account = StripeForeignKey(
        "Account",
        on_delete=models.PROTECT,
        null=True,
        blank=True,
        related_name="cards",
        help_text=(
            "The external account the charge was made on behalf of. Null here indicates"
            " that this value was never set."
        ),
    )
    address_city = models.TextField(
        max_length=5000,
        blank=True,
        default="",
        help_text="City/District/Suburb/Town/Village.",
    )
    address_country = models.TextField(
        max_length=5000, blank=True, default="", help_text="Billing address country."
    )
    address_line1 = models.TextField(
        max_length=5000,
        blank=True,
        default="",
        help_text="Street address/PO Box/Company name.",
    )
    address_line1_check = StripeEnumField(
        enum=enums.CardCheckResult,
        blank=True,
        default="",
        help_text="If `address_line1` was provided, results of the check.",
    )
    address_line2 = models.TextField(
        max_length=5000,
        blank=True,
        default="",
        help_text="Apartment/Suite/Unit/Building.",
    )
    address_state = models.TextField(
        max_length=5000,
        blank=True,
        default="",
        help_text="State/County/Province/Region.",
    )
    address_zip = models.TextField(
        max_length=5000, blank=True, default="", help_text="ZIP or postal code."
    )
    address_zip_check = StripeEnumField(
        enum=enums.CardCheckResult,
        blank=True,
        default="",
        help_text="If `address_zip` was provided, results of the check.",
    )
    brand = StripeEnumField(enum=enums.CardBrand, help_text="Card brand.")
    country = models.CharField(
        max_length=2,
        default="",
        blank=True,
        help_text="Two-letter ISO code representing the country of the card.",
    )
    customer = StripeForeignKey(
        "Customer", on_delete=models.SET_NULL, null=True, related_name="legacy_cards"
    )
    cvc_check = StripeEnumField(
        enum=enums.CardCheckResult,
        default="",
        blank=True,
        help_text="If a CVC was provided, results of the check.",
    )
    default_for_currency = models.BooleanField(
        null=True,
        help_text=(
            "Whether this external account (Card) is the default account for "
            "its currency."
        ),
    )
    dynamic_last4 = models.CharField(
        max_length=4,
        default="",
        blank=True,
        help_text=(
            "(For tokenized numbers only.) The last four digits of the device "
            "account number."
        ),
    )
    exp_month = models.IntegerField(help_text="Card expiration month.")
    exp_year = models.IntegerField(help_text="Card expiration year.")
    fingerprint = models.CharField(
        default="",
        blank=True,
        max_length=16,
        help_text="Uniquely identifies this particular card number.",
    )
    funding = StripeEnumField(
        enum=enums.CardFundingType, help_text="Card funding type."
    )
    last4 = models.CharField(max_length=4, help_text="Last four digits of Card number.")
    name = models.TextField(
        max_length=5000, default="", blank=True, help_text="Cardholder name."
    )
    tokenization_method = StripeEnumField(
        enum=enums.CardTokenizationMethod,
        default="",
        blank=True,
        help_text="If the card number is tokenized, this is the method that was used.",
    )

    def __str__(self):
        default = False
        # prefer to show it by customer format if present
        if self.customer:
            default_source = self.customer.default_source
            default_payment_method = self.customer.default_payment_method

            if (default_payment_method and self.id == default_payment_method.id) or (
                default_source and self.id == default_source.id
            ):
                # current card is the default payment method or source
                default = True

            customer_template = (
                f"{enums.CardBrand.humanize(self.brand)} {self.last4} {'Default' if default else ''} Expires"
                f" {self.exp_month} {self.exp_year}"
            )
            return customer_template

        elif self.account:
            default = getattr(self, "default_for_currency", False)
            account_template = f"{enums.CardBrand.humanize(self.brand)} {self.account.default_currency} {'Default' if default else ''} {self.last4}"
            return account_template

        return self.id or ""

    @classmethod
    def create_token(
        cls,
        number: str,
        exp_month: int,
        exp_year: int,
        cvc: str,
        api_key: str = djstripe_settings.STRIPE_SECRET_KEY,
        **kwargs,
    ) -> stripe.Token:
        """
        Creates a single use token that wraps the details of a credit card.
        This token can be used in place of a credit card dictionary with any API method.
        These tokens can only be used once: by creating a new charge object,
        or attaching them to a customer.
        (Source: https://stripe.com/docs/api?lang=python#create_card_token)

        :param number: The card number without any separators (no spaces)
        :param exp_month: The card's expiration month. (two digits)
        :param exp_year: The card's expiration year. (four digits)
        :param cvc: Card security code.
        :param api_key: The API key to use
        """

        card = {
            "number": number,
            "exp_month": exp_month,
            "exp_year": exp_year,
            "cvc": cvc,
        }
        card.update(kwargs)

        return stripe.Token.create(api_key=api_key, card=card)

Attributes

djstripe.models.payment_methods.Card.account = StripeForeignKey('Account', on_delete=models.PROTECT, null=True, blank=True, related_name='cards', help_text='The external account the charge was made on behalf of. Null here indicates that this value was never set.') class-attribute instance-attribute
djstripe.models.payment_methods.Card.address_city = models.TextField(max_length=5000, blank=True, default='', help_text='City/District/Suburb/Town/Village.') class-attribute instance-attribute
djstripe.models.payment_methods.Card.address_country = models.TextField(max_length=5000, blank=True, default='', help_text='Billing address country.') class-attribute instance-attribute
djstripe.models.payment_methods.Card.address_line1 = models.TextField(max_length=5000, blank=True, default='', help_text='Street address/PO Box/Company name.') class-attribute instance-attribute
djstripe.models.payment_methods.Card.address_line1_check = StripeEnumField(enum=enums.CardCheckResult, blank=True, default='', help_text='If `address_line1` was provided, results of the check.') class-attribute instance-attribute
djstripe.models.payment_methods.Card.address_line2 = models.TextField(max_length=5000, blank=True, default='', help_text='Apartment/Suite/Unit/Building.') class-attribute instance-attribute
djstripe.models.payment_methods.Card.address_state = models.TextField(max_length=5000, blank=True, default='', help_text='State/County/Province/Region.') class-attribute instance-attribute
djstripe.models.payment_methods.Card.address_zip = models.TextField(max_length=5000, blank=True, default='', help_text='ZIP or postal code.') class-attribute instance-attribute
djstripe.models.payment_methods.Card.address_zip_check = StripeEnumField(enum=enums.CardCheckResult, blank=True, default='', help_text='If `address_zip` was provided, results of the check.') class-attribute instance-attribute
djstripe.models.payment_methods.Card.brand = StripeEnumField(enum=enums.CardBrand, help_text='Card brand.') class-attribute instance-attribute
djstripe.models.payment_methods.Card.country = models.CharField(max_length=2, default='', blank=True, help_text='Two-letter ISO code representing the country of the card.') class-attribute instance-attribute
djstripe.models.payment_methods.Card.customer = StripeForeignKey('Customer', on_delete=models.SET_NULL, null=True, related_name='legacy_cards') class-attribute instance-attribute
djstripe.models.payment_methods.Card.cvc_check = StripeEnumField(enum=enums.CardCheckResult, default='', blank=True, help_text='If a CVC was provided, results of the check.') class-attribute instance-attribute
djstripe.models.payment_methods.Card.default_for_currency = models.BooleanField(null=True, help_text='Whether this external account (Card) is the default account for its currency.') class-attribute instance-attribute
djstripe.models.payment_methods.Card.dynamic_last4 = models.CharField(max_length=4, default='', blank=True, help_text='(For tokenized numbers only.) The last four digits of the device account number.') class-attribute instance-attribute
djstripe.models.payment_methods.Card.exp_month = models.IntegerField(help_text='Card expiration month.') class-attribute instance-attribute
djstripe.models.payment_methods.Card.exp_year = models.IntegerField(help_text='Card expiration year.') class-attribute instance-attribute
djstripe.models.payment_methods.Card.fingerprint = models.CharField(default='', blank=True, max_length=16, help_text='Uniquely identifies this particular card number.') class-attribute instance-attribute
djstripe.models.payment_methods.Card.funding = StripeEnumField(enum=enums.CardFundingType, help_text='Card funding type.') class-attribute instance-attribute
djstripe.models.payment_methods.Card.last4 = models.CharField(max_length=4, help_text='Last four digits of Card number.') class-attribute instance-attribute
djstripe.models.payment_methods.Card.name = models.TextField(max_length=5000, default='', blank=True, help_text='Cardholder name.') class-attribute instance-attribute
djstripe.models.payment_methods.Card.stripe_class = stripe.Card class-attribute instance-attribute
djstripe.models.payment_methods.Card.tokenization_method = StripeEnumField(enum=enums.CardTokenizationMethod, default='', blank=True, help_text='If the card number is tokenized, this is the method that was used.') class-attribute instance-attribute

Functions

djstripe.models.payment_methods.Card.__str__()
Source code in djstripe/models/payment_methods.py
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
def __str__(self):
    default = False
    # prefer to show it by customer format if present
    if self.customer:
        default_source = self.customer.default_source
        default_payment_method = self.customer.default_payment_method

        if (default_payment_method and self.id == default_payment_method.id) or (
            default_source and self.id == default_source.id
        ):
            # current card is the default payment method or source
            default = True

        customer_template = (
            f"{enums.CardBrand.humanize(self.brand)} {self.last4} {'Default' if default else ''} Expires"
            f" {self.exp_month} {self.exp_year}"
        )
        return customer_template

    elif self.account:
        default = getattr(self, "default_for_currency", False)
        account_template = f"{enums.CardBrand.humanize(self.brand)} {self.account.default_currency} {'Default' if default else ''} {self.last4}"
        return account_template

    return self.id or ""
djstripe.models.payment_methods.Card.create_token(number, exp_month, exp_year, cvc, api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs) classmethod

Creates a single use token that wraps the details of a credit card. This token can be used in place of a credit card dictionary with any API method. These tokens can only be used once: by creating a new charge object, or attaching them to a customer. (Source: https://stripe.com/docs/api?lang=python#create_card_token)

:param number: The card number without any separators (no spaces) :param exp_month: The card's expiration month. (two digits) :param exp_year: The card's expiration year. (four digits) :param cvc: Card security code. :param api_key: The API key to use

Source code in djstripe/models/payment_methods.py
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
@classmethod
def create_token(
    cls,
    number: str,
    exp_month: int,
    exp_year: int,
    cvc: str,
    api_key: str = djstripe_settings.STRIPE_SECRET_KEY,
    **kwargs,
) -> stripe.Token:
    """
    Creates a single use token that wraps the details of a credit card.
    This token can be used in place of a credit card dictionary with any API method.
    These tokens can only be used once: by creating a new charge object,
    or attaching them to a customer.
    (Source: https://stripe.com/docs/api?lang=python#create_card_token)

    :param number: The card number without any separators (no spaces)
    :param exp_month: The card's expiration month. (two digits)
    :param exp_year: The card's expiration year. (four digits)
    :param cvc: Card security code.
    :param api_key: The API key to use
    """

    card = {
        "number": number,
        "exp_month": exp_month,
        "exp_year": exp_year,
        "cvc": cvc,
    }
    card.update(kwargs)

    return stripe.Token.create(api_key=api_key, card=card)

djstripe.models.payment_methods.DjstripePaymentMethod

Bases: Model

An internal model that abstracts the legacy Card and BankAccount objects with Source objects.

Contains two fields: id and type: - id is the id of the Stripe object. - type can be card, bank_account account or source.

Source code in djstripe/models/payment_methods.py
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
class DjstripePaymentMethod(models.Model):
    """
    An internal model that abstracts the legacy Card and BankAccount
    objects with Source objects.

    Contains two fields: `id` and `type`:
    - `id` is the id of the Stripe object.
    - `type` can be `card`, `bank_account` `account` or `source`.
    """

    id = models.CharField(max_length=255, primary_key=True)
    type = models.CharField(max_length=50, db_index=True)

    @classmethod
    def from_stripe_object(cls, data):
        source_type = data["object"]
        model = cls._model_for_type(source_type)

        with transaction.atomic():
            model.sync_from_stripe_data(data)
            instance, _ = cls.objects.get_or_create(
                id=data["id"], defaults={"type": source_type}
            )

        return instance

    @classmethod
    def _get_or_create_source(
        cls, data, source_type=None, api_key=djstripe_settings.STRIPE_SECRET_KEY
    ):
        # prefer passed in source_type
        if not source_type:
            source_type = data["object"]

        try:
            model = cls._model_for_type(source_type)
            model._get_or_create_from_stripe_object(data, api_key=api_key)
        except ValueError as e:
            # This may happen if we have source types we don't know about.
            # Let's not make dj-stripe entirely unusable if that happens.
            logger.warning("Could not sync source of type %r: %s", source_type, e)

        return cls.objects.get_or_create(id=data["id"], defaults={"type": source_type})

    @classmethod
    def _model_for_type(cls, type):
        if type == "card":
            return Card
        elif type == "source":
            return Source
        elif type == "bank_account":
            return BankAccount
        elif type == "account":
            return Account

        raise ValueError(f"Unknown source type: {type}")

    @property
    def object_model(self):
        return self._model_for_type(self.type)

    def resolve(self):
        return self.object_model.objects.get(id=self.id)

    @classmethod
    def _get_or_create_from_stripe_object(
        cls,
        data,
        field_name="id",
        refetch=True,
        current_ids=None,
        pending_relations=None,
        save=True,
        stripe_account=None,
        api_key=djstripe_settings.STRIPE_SECRET_KEY,
    ):
        raw_field_data = data.get(field_name)
        id_ = get_id_from_stripe_data(raw_field_data)
        if not id_:
            raise ValueError(f"ID not found in Stripe data: {raw_field_data!r}")

        if id_.startswith("card"):
            source_cls = Card
            source_type = "card"
        elif id_.startswith("src"):
            source_cls = Source
            source_type = "source"
        elif id_.startswith("ba"):
            source_cls = BankAccount
            source_type = "bank_account"
        elif id_.startswith("acct"):
            source_cls = Account
            source_type = "account"
        else:
            # This may happen if we have source types we don't know about.
            # Let's not make dj-stripe entirely unusable if that happens.
            logger.warning(f"Unknown Object. Could not sync source with id: {id_}")
            return cls.objects.get_or_create(
                id=id_, defaults={"type": f"UNSUPPORTED_{id_}"}
            )

        # call model's _get_or_create_from_stripe_object to ensure
        # that object exists before getting or creating its source object
        source_cls._get_or_create_from_stripe_object(
            data,
            field_name,
            refetch=refetch,
            current_ids=current_ids,
            pending_relations=pending_relations,
            stripe_account=stripe_account,
            api_key=api_key,
        )

        return cls.objects.get_or_create(id=id_, defaults={"type": source_type})

Attributes

djstripe.models.payment_methods.DjstripePaymentMethod.id = models.CharField(max_length=255, primary_key=True) class-attribute instance-attribute
djstripe.models.payment_methods.DjstripePaymentMethod.object_model property
djstripe.models.payment_methods.DjstripePaymentMethod.type = models.CharField(max_length=50, db_index=True) class-attribute instance-attribute

Functions

djstripe.models.payment_methods.DjstripePaymentMethod.from_stripe_object(data) classmethod
Source code in djstripe/models/payment_methods.py
36
37
38
39
40
41
42
43
44
45
46
47
@classmethod
def from_stripe_object(cls, data):
    source_type = data["object"]
    model = cls._model_for_type(source_type)

    with transaction.atomic():
        model.sync_from_stripe_data(data)
        instance, _ = cls.objects.get_or_create(
            id=data["id"], defaults={"type": source_type}
        )

    return instance
djstripe.models.payment_methods.DjstripePaymentMethod.resolve()
Source code in djstripe/models/payment_methods.py
84
85
def resolve(self):
    return self.object_model.objects.get(id=self.id)

djstripe.models.payment_methods.LegacySourceMixin

Mixin for functionality shared between the legacy Card & BankAccount sources

Source code in djstripe/models/payment_methods.py
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
class LegacySourceMixin:
    """
    Mixin for functionality shared between the legacy Card & BankAccount sources
    """

    customer: Optional[StripeForeignKey]
    account: Optional[StripeForeignKey]
    id: str
    default_api_key: str

    @classmethod
    def _get_customer_or_account_from_kwargs(cls, **kwargs):
        account = kwargs.get("account")
        customer = kwargs.get("customer")

        if not account and not customer:
            raise StripeObjectManipulationException(
                f"{cls.__name__} objects must be manipulated through either a "
                "Stripe Connected Account or a customer. "
                "Pass a Customer or an Account object into this call."
            )

        if account and not isinstance(account, Account):
            raise StripeObjectManipulationException(
                f"{cls.__name__} objects must be manipulated through a Stripe Connected"
                " Account. Pass an Account object into this call."
            )

        if customer and not isinstance(customer, Customer):
            raise StripeObjectManipulationException(
                f"{cls.__name__} objects must be manipulated through a Customer. "
                "Pass a Customer object into this call."
            )

        if account:
            del kwargs["account"]
        if customer:
            del kwargs["customer"]

        return account, customer, kwargs

    @classmethod
    def _api_create(cls, api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs):
        # OVERRIDING the parent version of this function
        # Cards & Bank Accounts must be manipulated through a customer or account.

        account, customer, clean_kwargs = cls._get_customer_or_account_from_kwargs(
            **kwargs
        )

        # First we try to retrieve by customer attribute,
        # then by account attribute
        if customer and account:
            try:
                # retrieve by customer
                return customer.api_retrieve(api_key=api_key).sources.create(
                    api_key=api_key, **clean_kwargs
                )
            except Exception as customer_exc:
                try:
                    # retrieve by account
                    return account.api_retrieve(
                        api_key=api_key
                    ).external_accounts.create(api_key=api_key, **clean_kwargs)
                except Exception:
                    raise customer_exc

        if customer:
            return customer.api_retrieve(api_key=api_key).sources.create(
                api_key=api_key, **clean_kwargs
            )

        if account:
            return account.api_retrieve(api_key=api_key).external_accounts.create(
                api_key=api_key, **clean_kwargs
            )

    @classmethod
    def api_list(cls, api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs):
        # OVERRIDING the parent version of this function
        # Cards & Bank Accounts must be manipulated through a customer or account.

        # Update kwargs with `expand` param
        kwargs = cls.get_expand_params(api_key, **kwargs)

        account, customer, clean_kwargs = cls._get_customer_or_account_from_kwargs(
            **kwargs
        )

        object_name = cls.stripe_class.OBJECT_NAME

        # First we try to retrieve by customer attribute,
        # then by account attribute
        if customer and account:
            try:
                # retrieve by customer
                return (
                    customer.api_retrieve(api_key=api_key)
                    .sources.list(object=object_name, **clean_kwargs)
                    .auto_paging_iter()
                )
            except Exception as customer_exc:
                try:
                    # retrieve by account
                    return (
                        account.api_retrieve(api_key=api_key)
                        .external_accounts.list(object=object_name, **clean_kwargs)
                        .auto_paging_iter()
                    )
                except Exception:
                    raise customer_exc

        if customer:
            return (
                customer.api_retrieve(api_key=api_key)
                .sources.list(object=object_name, **clean_kwargs)
                .auto_paging_iter()
            )

        if account:
            return (
                account.api_retrieve(api_key=api_key)
                .external_accounts.list(object=object_name, **clean_kwargs)
                .auto_paging_iter()
            )

        raise ImpossibleAPIRequest(
            f"Can't list {object_name} without a customer or account object. This may"
            " happen if not all accounts or customer objects are in the db. Please run"
            ' "python manage.py djstripe_sync_models Account Customer" as a potential'
            " fix."
        )

    def get_stripe_dashboard_url(self) -> str:
        if self.customer:
            return self.customer.get_stripe_dashboard_url()
        elif self.account:
            return f"https://dashboard.stripe.com/{self.account.id}/settings/payouts"
        else:
            return ""

    def remove(self):
        """
        Removes a legacy source from this customer's account.
        """

        # First, wipe default source on all customers that use this card.
        Customer.objects.filter(default_source=self.id).update(default_source=None)

        try:
            self._api_delete()
        except InvalidRequestError as exc:
            if "No such source:" in str(exc) or "No such customer:" in str(exc):
                # The exception was thrown because the stripe customer or card
                # was already deleted on the stripe side, ignore the exception
                pass
            else:
                # The exception was raised for another reason, re-raise it
                raise

        self.delete()

    def api_retrieve(self, api_key=None, stripe_account=None):
        # OVERRIDING the parent version of this function
        # Cards & Banks Accounts must be manipulated through a customer or account.

        api_key = api_key or self.default_api_key

        if self.customer:
            return stripe.Customer.retrieve_source(
                self.customer.id,
                self.id,
                expand=self.expand_fields,
                stripe_account=stripe_account,
                api_key=api_key,
                stripe_version=djstripe_settings.STRIPE_API_VERSION,
            )

        # try to retrieve by account attribute if retrieval by customer fails.
        if self.account:
            return stripe.Account.retrieve_external_account(
                self.account.id,
                self.id,
                expand=self.expand_fields,
                stripe_account=stripe_account,
                api_key=api_key,
                stripe_version=djstripe_settings.STRIPE_API_VERSION,
            )

        raise ImpossibleAPIRequest(
            f"Can't retrieve {self.__class__} without a customer or account object."
            " This may happen if not all accounts or customer objects are in the db."
            ' Please run "python manage.py djstripe_sync_models Account Customer" as a'
            " potential fix."
        )

    def _api_delete(self, api_key=None, stripe_account=None, **kwargs):
        # OVERRIDING the parent version of this function
        # Cards & Banks Accounts must be manipulated through a customer or account.

        api_key = api_key or self.default_api_key
        # Prefer passed in stripe_account if set.
        if not stripe_account:
            stripe_account = self._get_stripe_account_id(api_key)

        if self.customer:
            return stripe.Customer.delete_source(
                self.customer.id,
                self.id,
                api_key=api_key,
                stripe_account=stripe_account,
                **kwargs,
            )

        if self.account:
            return stripe.Account.delete_external_account(
                self.account.id,
                self.id,
                api_key=api_key,
                stripe_account=stripe_account,
                **kwargs,
            )

        raise ImpossibleAPIRequest(
            f"Can't delete {self.__class__} without a customer or account object. This"
            " may happen if not all accounts or customer objects are in the db. Please"
            ' run "python manage.py djstripe_sync_models Account Customer" as a'
            " potential fix."
        )

Attributes

djstripe.models.payment_methods.LegacySourceMixin.account: Optional[StripeForeignKey] instance-attribute
djstripe.models.payment_methods.LegacySourceMixin.customer: Optional[StripeForeignKey] instance-attribute
djstripe.models.payment_methods.LegacySourceMixin.default_api_key: str instance-attribute
djstripe.models.payment_methods.LegacySourceMixin.id: str instance-attribute

Functions

djstripe.models.payment_methods.LegacySourceMixin.api_list(api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs) classmethod
Source code in djstripe/models/payment_methods.py
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
@classmethod
def api_list(cls, api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs):
    # OVERRIDING the parent version of this function
    # Cards & Bank Accounts must be manipulated through a customer or account.

    # Update kwargs with `expand` param
    kwargs = cls.get_expand_params(api_key, **kwargs)

    account, customer, clean_kwargs = cls._get_customer_or_account_from_kwargs(
        **kwargs
    )

    object_name = cls.stripe_class.OBJECT_NAME

    # First we try to retrieve by customer attribute,
    # then by account attribute
    if customer and account:
        try:
            # retrieve by customer
            return (
                customer.api_retrieve(api_key=api_key)
                .sources.list(object=object_name, **clean_kwargs)
                .auto_paging_iter()
            )
        except Exception as customer_exc:
            try:
                # retrieve by account
                return (
                    account.api_retrieve(api_key=api_key)
                    .external_accounts.list(object=object_name, **clean_kwargs)
                    .auto_paging_iter()
                )
            except Exception:
                raise customer_exc

    if customer:
        return (
            customer.api_retrieve(api_key=api_key)
            .sources.list(object=object_name, **clean_kwargs)
            .auto_paging_iter()
        )

    if account:
        return (
            account.api_retrieve(api_key=api_key)
            .external_accounts.list(object=object_name, **clean_kwargs)
            .auto_paging_iter()
        )

    raise ImpossibleAPIRequest(
        f"Can't list {object_name} without a customer or account object. This may"
        " happen if not all accounts or customer objects are in the db. Please run"
        ' "python manage.py djstripe_sync_models Account Customer" as a potential'
        " fix."
    )
djstripe.models.payment_methods.LegacySourceMixin.api_retrieve(api_key=None, stripe_account=None)
Source code in djstripe/models/payment_methods.py
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
def api_retrieve(self, api_key=None, stripe_account=None):
    # OVERRIDING the parent version of this function
    # Cards & Banks Accounts must be manipulated through a customer or account.

    api_key = api_key or self.default_api_key

    if self.customer:
        return stripe.Customer.retrieve_source(
            self.customer.id,
            self.id,
            expand=self.expand_fields,
            stripe_account=stripe_account,
            api_key=api_key,
            stripe_version=djstripe_settings.STRIPE_API_VERSION,
        )

    # try to retrieve by account attribute if retrieval by customer fails.
    if self.account:
        return stripe.Account.retrieve_external_account(
            self.account.id,
            self.id,
            expand=self.expand_fields,
            stripe_account=stripe_account,
            api_key=api_key,
            stripe_version=djstripe_settings.STRIPE_API_VERSION,
        )

    raise ImpossibleAPIRequest(
        f"Can't retrieve {self.__class__} without a customer or account object."
        " This may happen if not all accounts or customer objects are in the db."
        ' Please run "python manage.py djstripe_sync_models Account Customer" as a'
        " potential fix."
    )
djstripe.models.payment_methods.LegacySourceMixin.get_stripe_dashboard_url()
Source code in djstripe/models/payment_methods.py
272
273
274
275
276
277
278
def get_stripe_dashboard_url(self) -> str:
    if self.customer:
        return self.customer.get_stripe_dashboard_url()
    elif self.account:
        return f"https://dashboard.stripe.com/{self.account.id}/settings/payouts"
    else:
        return ""
djstripe.models.payment_methods.LegacySourceMixin.remove()

Removes a legacy source from this customer's account.

Source code in djstripe/models/payment_methods.py
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
def remove(self):
    """
    Removes a legacy source from this customer's account.
    """

    # First, wipe default source on all customers that use this card.
    Customer.objects.filter(default_source=self.id).update(default_source=None)

    try:
        self._api_delete()
    except InvalidRequestError as exc:
        if "No such source:" in str(exc) or "No such customer:" in str(exc):
            # The exception was thrown because the stripe customer or card
            # was already deleted on the stripe side, ignore the exception
            pass
        else:
            # The exception was raised for another reason, re-raise it
            raise

    self.delete()

djstripe.models.payment_methods.PaymentMethod

Bases: StripeModel

PaymentMethod objects represent your customer's payment instruments. You can use them with PaymentIntents to collect payments or save them to Customer objects to store instrument details for future payments.

Stripe documentation: https://stripe.com/docs/api?lang=python#payment_methods

Source code in djstripe/models/payment_methods.py
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
class PaymentMethod(StripeModel):
    """
    PaymentMethod objects represent your customer's payment instruments.
    You can use them with PaymentIntents to collect payments or save them
    to Customer objects to store instrument details for future payments.

    Stripe documentation: https://stripe.com/docs/api?lang=python#payment_methods
    """

    stripe_class = stripe.PaymentMethod
    description = None

    billing_details = JSONField(
        help_text=(
            "Billing information associated with the PaymentMethod that may be used or "
            "required by particular types of payment methods."
        )
    )
    customer = StripeForeignKey(
        "Customer",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="payment_methods",
        help_text=(
            "Customer to which this PaymentMethod is saved. "
            "This will not be set when the PaymentMethod has "
            "not been saved to a Customer."
        ),
    )
    type = StripeEnumField(
        enum=enums.PaymentMethodType,
        help_text="The type of the PaymentMethod.",
    )
    acss_debit = JSONField(
        null=True,
        blank=True,
        help_text="Additional information for payment methods of type `acss_debit`",
    )
    affirm = JSONField(
        null=True,
        blank=True,
        help_text="Additional information for payment methods of type `affirm`",
    )
    afterpay_clearpay = JSONField(
        null=True,
        blank=True,
        help_text=(
            "Additional information for payment methods of type `afterpay_clearpay`"
        ),
    )
    alipay = JSONField(
        null=True,
        blank=True,
        help_text="Additional information for payment methods of type `alipay`",
    )
    au_becs_debit = JSONField(
        null=True,
        blank=True,
        help_text="Additional information for payment methods of type `au_becs_debit`",
    )
    bacs_debit = JSONField(
        null=True,
        blank=True,
        help_text="Additional information for payment methods of type `bacs_debit`",
    )
    bancontact = JSONField(
        null=True,
        blank=True,
        help_text="Additional information for payment methods of type `bancontact`",
    )
    blik = JSONField(
        null=True,
        blank=True,
        help_text="Additional information for payment methods of type `blik`",
    )
    boleto = JSONField(
        null=True,
        blank=True,
        help_text="Additional information for payment methods of type `boleto`",
    )
    card = JSONField(
        null=True,
        blank=True,
        help_text="Additional information for payment methods of type `card`",
    )
    card_present = JSONField(
        null=True,
        blank=True,
        help_text="Additional information for payment methods of type `card_present`",
    )
    customer_balance = JSONField(
        null=True,
        blank=True,
        help_text=(
            "Additional information for payment methods of type `customer_balance`"
        ),
    )
    eps = JSONField(
        null=True,
        blank=True,
        help_text="Additional information for payment methods of type `eps`",
    )
    fpx = JSONField(
        null=True,
        blank=True,
        help_text="Additional information for payment methods of type `fpx`",
    )
    giropay = JSONField(
        null=True,
        blank=True,
        help_text="Additional information for payment methods of type `giropay`",
    )
    grabpay = JSONField(
        null=True,
        blank=True,
        help_text="Additional information for payment methods of type `grabpay`",
    )
    ideal = JSONField(
        null=True,
        blank=True,
        help_text="Additional information for payment methods of type `ideal`",
    )
    interac_present = JSONField(
        null=True,
        blank=True,
        help_text=(
            "Additional information for payment methods of type `interac_present`"
        ),
    )
    klarna = JSONField(
        null=True,
        blank=True,
        help_text="Additional information for payment methods of type `klarna`",
    )
    konbini = JSONField(
        null=True,
        blank=True,
        help_text="Additional information for payment methods of type `konbini`",
    )
    link = JSONField(
        null=True,
        blank=True,
        help_text="Additional information for payment methods of type `link`",
    )
    oxxo = JSONField(
        null=True,
        blank=True,
        help_text="Additional information for payment methods of type `oxxo`",
    )
    p24 = JSONField(
        null=True,
        blank=True,
        help_text="Additional information for payment methods of type `p24`",
    )
    paynow = JSONField(
        null=True,
        blank=True,
        help_text="Additional information for payment methods of type `paynow`",
    )
    pix = JSONField(
        null=True,
        blank=True,
        help_text="Additional information for payment methods of type `pix`",
    )
    promptpay = JSONField(
        null=True,
        blank=True,
        help_text="Additional information for payment methods of type `promptpay`",
    )
    sepa_debit = JSONField(
        null=True,
        blank=True,
        help_text="Additional information for payment methods of type `sepa_debit`",
    )
    sofort = JSONField(
        null=True,
        blank=True,
        help_text="Additional information for payment methods of type `sofort`",
    )
    us_bank_account = JSONField(
        null=True,
        blank=True,
        help_text=(
            "Additional information for payment methods of type `us_bank_account`"
        ),
    )
    wechat_pay = JSONField(
        null=True,
        blank=True,
        help_text="Additional information for payment methods of type `wechat_pay`",
    )

    def __str__(self):
        if self.customer:
            return f"{enums.PaymentMethodType.humanize(self.type)} for {self.customer}"
        return (
            f"{enums.PaymentMethodType.humanize(self.type)} is not associated with any"
            " customer"
        )

    def get_stripe_dashboard_url(self) -> str:
        if self.customer:
            return self.customer.get_stripe_dashboard_url()
        return ""

    def _attach_objects_hook(
        self, cls, data, api_key=djstripe_settings.STRIPE_SECRET_KEY, current_ids=None
    ):
        customer = None
        # "customer" key could be like "cus_6lsBvm5rJ0zyHc" or {"id": "cus_6lsBvm5rJ0zyHc"}
        customer_id = get_id_from_stripe_data(data.get("customer"))

        if current_ids is None or customer_id not in current_ids:
            customer = cls._stripe_object_to_customer(
                target_cls=Customer, data=data, current_ids=current_ids, api_key=api_key
            )

        if customer:
            self.customer = customer
        else:
            self.customer = None

    @classmethod
    def attach(
        cls,
        payment_method: Union[str, "PaymentMethod"],
        customer: Union[str, Customer],
        api_key: str = djstripe_settings.STRIPE_SECRET_KEY,
    ) -> "PaymentMethod":
        """
        Attach a payment method to a customer
        """

        if isinstance(payment_method, StripeModel):
            payment_method = payment_method.id

        if isinstance(customer, StripeModel):
            customer = customer.id

        extra_kwargs = {}
        if not isinstance(payment_method, stripe.PaymentMethod):
            # send api_key if we're not passing in a Stripe object
            # avoids "Received unknown parameter: api_key" since api uses the
            # key cached in the Stripe object
            extra_kwargs = {"api_key": api_key}

        stripe_payment_method = stripe.PaymentMethod.attach(
            payment_method, customer=customer, **extra_kwargs
        )
        return cls.sync_from_stripe_data(stripe_payment_method, api_key=api_key)

    def detach(self):
        """
        Detach the payment method from its customer.

        :return: Returns true if the payment method was newly detached, \
                 false if it was already detached
        :rtype: bool
        """
        # Find customers that use this
        customers = Customer.objects.filter(default_payment_method=self).all()
        changed = True

        # special handling is needed for legacy "card"-type PaymentMethods,
        # since detaching them deletes them within Stripe.
        # see https://github.com/dj-stripe/dj-stripe/pull/967
        is_legacy_card = self.id.startswith("card_")

        try:
            self.sync_from_stripe_data(self.api_retrieve().detach())

            # resync customer to update .default_payment_method and
            # .invoice_settings.default_payment_method
            for customer in customers:
                Customer.sync_from_stripe_data(customer.api_retrieve())

        except (InvalidRequestError,):
            # The source was already detached. Resyncing.

            if self.pk and not is_legacy_card:
                self.sync_from_stripe_data(self.api_retrieve())
            changed = False

        if self.pk:
            if is_legacy_card:
                self.delete()
            else:
                self.refresh_from_db()

        return changed

Attributes

djstripe.models.payment_methods.PaymentMethod.acss_debit = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `acss_debit`') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.affirm = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `affirm`') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.afterpay_clearpay = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `afterpay_clearpay`') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.alipay = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `alipay`') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.au_becs_debit = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `au_becs_debit`') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.bacs_debit = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `bacs_debit`') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.bancontact = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `bancontact`') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.billing_details = JSONField(help_text='Billing information associated with the PaymentMethod that may be used or required by particular types of payment methods.') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.blik = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `blik`') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.boleto = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `boleto`') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.card = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `card`') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.card_present = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `card_present`') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.customer = StripeForeignKey('Customer', on_delete=models.SET_NULL, null=True, blank=True, related_name='payment_methods', help_text='Customer to which this PaymentMethod is saved. This will not be set when the PaymentMethod has not been saved to a Customer.') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.customer_balance = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `customer_balance`') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.description = None class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.eps = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `eps`') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.fpx = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `fpx`') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.giropay = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `giropay`') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.grabpay = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `grabpay`') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.ideal = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `ideal`') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.interac_present = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `interac_present`') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.klarna = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `klarna`') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.konbini = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `konbini`') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.oxxo = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `oxxo`') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.p24 = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `p24`') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.paynow = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `paynow`') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.pix = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `pix`') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.promptpay = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `promptpay`') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.sepa_debit = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `sepa_debit`') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.sofort = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `sofort`') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.stripe_class = stripe.PaymentMethod class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.type = StripeEnumField(enum=enums.PaymentMethodType, help_text='The type of the PaymentMethod.') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.us_bank_account = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `us_bank_account`') class-attribute instance-attribute
djstripe.models.payment_methods.PaymentMethod.wechat_pay = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `wechat_pay`') class-attribute instance-attribute

Functions

djstripe.models.payment_methods.PaymentMethod.__str__()
Source code in djstripe/models/payment_methods.py
1133
1134
1135
1136
1137
1138
1139
def __str__(self):
    if self.customer:
        return f"{enums.PaymentMethodType.humanize(self.type)} for {self.customer}"
    return (
        f"{enums.PaymentMethodType.humanize(self.type)} is not associated with any"
        " customer"
    )
djstripe.models.payment_methods.PaymentMethod.attach(payment_method, customer, api_key=djstripe_settings.STRIPE_SECRET_KEY) classmethod

Attach a payment method to a customer

Source code in djstripe/models/payment_methods.py
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
@classmethod
def attach(
    cls,
    payment_method: Union[str, "PaymentMethod"],
    customer: Union[str, Customer],
    api_key: str = djstripe_settings.STRIPE_SECRET_KEY,
) -> "PaymentMethod":
    """
    Attach a payment method to a customer
    """

    if isinstance(payment_method, StripeModel):
        payment_method = payment_method.id

    if isinstance(customer, StripeModel):
        customer = customer.id

    extra_kwargs = {}
    if not isinstance(payment_method, stripe.PaymentMethod):
        # send api_key if we're not passing in a Stripe object
        # avoids "Received unknown parameter: api_key" since api uses the
        # key cached in the Stripe object
        extra_kwargs = {"api_key": api_key}

    stripe_payment_method = stripe.PaymentMethod.attach(
        payment_method, customer=customer, **extra_kwargs
    )
    return cls.sync_from_stripe_data(stripe_payment_method, api_key=api_key)
djstripe.models.payment_methods.PaymentMethod.detach()

Detach the payment method from its customer.

:return: Returns true if the payment method was newly detached, false if it was already detached :rtype: bool

Source code in djstripe/models/payment_methods.py
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
def detach(self):
    """
    Detach the payment method from its customer.

    :return: Returns true if the payment method was newly detached, \
             false if it was already detached
    :rtype: bool
    """
    # Find customers that use this
    customers = Customer.objects.filter(default_payment_method=self).all()
    changed = True

    # special handling is needed for legacy "card"-type PaymentMethods,
    # since detaching them deletes them within Stripe.
    # see https://github.com/dj-stripe/dj-stripe/pull/967
    is_legacy_card = self.id.startswith("card_")

    try:
        self.sync_from_stripe_data(self.api_retrieve().detach())

        # resync customer to update .default_payment_method and
        # .invoice_settings.default_payment_method
        for customer in customers:
            Customer.sync_from_stripe_data(customer.api_retrieve())

    except (InvalidRequestError,):
        # The source was already detached. Resyncing.

        if self.pk and not is_legacy_card:
            self.sync_from_stripe_data(self.api_retrieve())
        changed = False

    if self.pk:
        if is_legacy_card:
            self.delete()
        else:
            self.refresh_from_db()

    return changed
djstripe.models.payment_methods.PaymentMethod.get_stripe_dashboard_url()
Source code in djstripe/models/payment_methods.py
1141
1142
1143
1144
def get_stripe_dashboard_url(self) -> str:
    if self.customer:
        return self.customer.get_stripe_dashboard_url()
    return ""

djstripe.models.payment_methods.Source

Bases: StripeModel

Source objects allow you to accept a variety of payment methods. They represent a customer's payment instrument, and can be used with the Stripe API just like a Card object: once chargeable, they can be charged, or can be attached to customers.

Stripe documentation: https://stripe.com/docs/api?lang=python#sources

Source code in djstripe/models/payment_methods.py
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
class Source(StripeModel):
    """
    Source objects allow you to accept a variety of payment methods.
    They represent a customer's payment instrument, and can be used with
    the Stripe API just like a Card object: once chargeable,
    they can be charged, or can be attached to customers.

    Stripe documentation: https://stripe.com/docs/api?lang=python#sources
    """

    amount = StripeDecimalCurrencyAmountField(
        null=True,
        blank=True,
        help_text=(
            "Amount (as decimal) associated with the source. "
            "This is the amount for which the source will be chargeable once ready. "
            "Required for `single_use` sources."
        ),
    )
    client_secret = models.CharField(
        max_length=255,
        help_text=(
            "The client secret of the source. "
            "Used for client-side retrieval using a publishable key."
        ),
    )
    currency = StripeCurrencyCodeField(default="", blank=True)
    flow = StripeEnumField(
        enum=enums.SourceFlow, help_text="The authentication flow of the source."
    )
    owner = JSONField(
        help_text=(
            "Information about the owner of the payment instrument that may be "
            "used or required by particular source types."
        )
    )
    statement_descriptor = models.CharField(
        max_length=255,
        default="",
        blank=True,
        help_text=(
            "Extra information about a source. This will appear on your "
            "customer's statement every time you charge the source."
        ),
    )
    status = StripeEnumField(
        enum=enums.SourceStatus,
        help_text=(
            "The status of the source. Only `chargeable` sources can be used "
            "to create a charge."
        ),
    )
    type = StripeEnumField(enum=enums.SourceType, help_text="The type of the source.")
    usage = StripeEnumField(
        enum=enums.SourceUsage,
        help_text=(
            "Whether this source should be reusable or not. "
            "Some source types may or may not be reusable by construction, "
            "while other may leave the option at creation."
        ),
    )

    # Flows
    code_verification = JSONField(
        null=True,
        blank=True,
        help_text=(
            "Information related to the code verification flow. "
            "Present if the source is authenticated by a verification code "
            "(`flow` is `code_verification`)."
        ),
    )
    receiver = JSONField(
        null=True,
        blank=True,
        help_text=(
            "Information related to the receiver flow. "
            "Present if the source is a receiver (`flow` is `receiver`)."
        ),
    )
    redirect = JSONField(
        null=True,
        blank=True,
        help_text=(
            "Information related to the redirect flow. Present if the source is"
            " authenticated by a redirect (`flow` is `redirect`)."
        ),
    )

    source_data = JSONField(help_text="The data corresponding to the source type.")

    customer = StripeForeignKey(
        "Customer",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="sources",
    )

    stripe_class = stripe.Source
    stripe_dashboard_item_name = "sources"

    def __str__(self):
        return f"{self.type} {self.id}"

    @classmethod
    def _manipulate_stripe_object_hook(cls, data):
        # The source_data dict is an alias of all the source types
        data["source_data"] = data[data["type"]]
        return data

    def _attach_objects_hook(
        self, cls, data, api_key=djstripe_settings.STRIPE_SECRET_KEY, current_ids=None
    ):
        customer = None
        # "customer" key could be like "cus_6lsBvm5rJ0zyHc" or {"id": "cus_6lsBvm5rJ0zyHc"}
        customer_id = get_id_from_stripe_data(data.get("customer"))

        if current_ids is None or customer_id not in current_ids:
            customer = cls._stripe_object_to_customer(
                target_cls=Customer, data=data, current_ids=current_ids, api_key=api_key
            )

        if customer:
            self.customer = customer
        else:
            self.customer = None

    def detach(self) -> bool:
        """
        Detach the source from its customer.
        """

        # First, wipe default source on all customers that use this.
        Customer.objects.filter(default_source=self.id).update(default_source=None)
        api_key = self.default_api_key
        try:
            # TODO - we could use the return value of sync_from_stripe_data
            #  or call its internals - self._sync/_attach_objects_hook etc here
            #  to update `self` at this point?
            self.sync_from_stripe_data(
                self.api_retrieve(api_key=api_key).detach(), api_key=api_key
            )
            return True
        except InvalidRequestError:
            # The source was already detached. Resyncing.
            self.sync_from_stripe_data(
                self.api_retrieve(api_key=self.default_api_key),
                api_key=self.default_api_key,
            )
            return False

    @classmethod
    def api_list(cls, api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs):
        """
        Call the stripe API's list operation for this model.
        :param api_key: The api key to use for this request. \
            Defaults to djstripe_settings.STRIPE_SECRET_KEY.
        :type api_key: string
        See Stripe documentation for accepted kwargs for each object.
        :returns: an iterator over all items in the query
        """
        # Update kwargs with `expand` param
        kwargs = cls.get_expand_params(api_key, **kwargs)

        return Customer.stripe_class.list_sources(
            object="source",
            api_key=api_key,
            stripe_version=djstripe_settings.STRIPE_API_VERSION,
            **kwargs,
        ).auto_paging_iter()

Attributes

djstripe.models.payment_methods.Source.amount = StripeDecimalCurrencyAmountField(null=True, blank=True, help_text='Amount (as decimal) associated with the source. This is the amount for which the source will be chargeable once ready. Required for `single_use` sources.') class-attribute instance-attribute
djstripe.models.payment_methods.Source.client_secret = models.CharField(max_length=255, help_text='The client secret of the source. Used for client-side retrieval using a publishable key.') class-attribute instance-attribute
djstripe.models.payment_methods.Source.code_verification = JSONField(null=True, blank=True, help_text='Information related to the code verification flow. Present if the source is authenticated by a verification code (`flow` is `code_verification`).') class-attribute instance-attribute
djstripe.models.payment_methods.Source.currency = StripeCurrencyCodeField(default='', blank=True) class-attribute instance-attribute
djstripe.models.payment_methods.Source.customer = StripeForeignKey('Customer', on_delete=models.SET_NULL, null=True, blank=True, related_name='sources') class-attribute instance-attribute
djstripe.models.payment_methods.Source.flow = StripeEnumField(enum=enums.SourceFlow, help_text='The authentication flow of the source.') class-attribute instance-attribute
djstripe.models.payment_methods.Source.owner = JSONField(help_text='Information about the owner of the payment instrument that may be used or required by particular source types.') class-attribute instance-attribute
djstripe.models.payment_methods.Source.receiver = JSONField(null=True, blank=True, help_text='Information related to the receiver flow. Present if the source is a receiver (`flow` is `receiver`).') class-attribute instance-attribute
djstripe.models.payment_methods.Source.redirect = JSONField(null=True, blank=True, help_text='Information related to the redirect flow. Present if the source is authenticated by a redirect (`flow` is `redirect`).') class-attribute instance-attribute
djstripe.models.payment_methods.Source.source_data = JSONField(help_text='The data corresponding to the source type.') class-attribute instance-attribute
djstripe.models.payment_methods.Source.statement_descriptor = models.CharField(max_length=255, default='', blank=True, help_text="Extra information about a source. This will appear on your customer's statement every time you charge the source.") class-attribute instance-attribute
djstripe.models.payment_methods.Source.status = StripeEnumField(enum=enums.SourceStatus, help_text='The status of the source. Only `chargeable` sources can be used to create a charge.') class-attribute instance-attribute
djstripe.models.payment_methods.Source.stripe_class = stripe.Source class-attribute instance-attribute
djstripe.models.payment_methods.Source.stripe_dashboard_item_name = 'sources' class-attribute instance-attribute
djstripe.models.payment_methods.Source.type = StripeEnumField(enum=enums.SourceType, help_text='The type of the source.') class-attribute instance-attribute
djstripe.models.payment_methods.Source.usage = StripeEnumField(enum=enums.SourceUsage, help_text='Whether this source should be reusable or not. Some source types may or may not be reusable by construction, while other may leave the option at creation.') class-attribute instance-attribute

Functions

djstripe.models.payment_methods.Source.__str__()
Source code in djstripe/models/payment_methods.py
767
768
def __str__(self):
    return f"{self.type} {self.id}"
djstripe.models.payment_methods.Source.api_list(api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs) classmethod

Call the stripe API's list operation for this model. :param api_key: The api key to use for this request. Defaults to djstripe_settings.STRIPE_SECRET_KEY. :type api_key: string See Stripe documentation for accepted kwargs for each object. :returns: an iterator over all items in the query

Source code in djstripe/models/payment_methods.py
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
@classmethod
def api_list(cls, api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs):
    """
    Call the stripe API's list operation for this model.
    :param api_key: The api key to use for this request. \
        Defaults to djstripe_settings.STRIPE_SECRET_KEY.
    :type api_key: string
    See Stripe documentation for accepted kwargs for each object.
    :returns: an iterator over all items in the query
    """
    # Update kwargs with `expand` param
    kwargs = cls.get_expand_params(api_key, **kwargs)

    return Customer.stripe_class.list_sources(
        object="source",
        api_key=api_key,
        stripe_version=djstripe_settings.STRIPE_API_VERSION,
        **kwargs,
    ).auto_paging_iter()
djstripe.models.payment_methods.Source.detach()

Detach the source from its customer.

Source code in djstripe/models/payment_methods.py
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
def detach(self) -> bool:
    """
    Detach the source from its customer.
    """

    # First, wipe default source on all customers that use this.
    Customer.objects.filter(default_source=self.id).update(default_source=None)
    api_key = self.default_api_key
    try:
        # TODO - we could use the return value of sync_from_stripe_data
        #  or call its internals - self._sync/_attach_objects_hook etc here
        #  to update `self` at this point?
        self.sync_from_stripe_data(
            self.api_retrieve(api_key=api_key).detach(), api_key=api_key
        )
        return True
    except InvalidRequestError:
        # The source was already detached. Resyncing.
        self.sync_from_stripe_data(
            self.api_retrieve(api_key=self.default_api_key),
            api_key=self.default_api_key,
        )
        return False

djstripe.models.payment_methods.SourceTransaction

Bases: StripeModel

Stripe documentation: https://stripe.com/docs/sources/ach-credit-transfer#source-transactions

Source code in djstripe/models/payment_methods.py
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
class SourceTransaction(StripeModel):
    """
    Stripe documentation: https://stripe.com/docs/sources/ach-credit-transfer#source-transactions
    """

    stripe_class = stripe.SourceTransaction
    stripe_dashboard_item_name = "source_transactions"

    description = None
    metadata = None
    type = enums.SourceType.ach_credit_transfer

    ach_credit_transfer = JSONField(
        help_text="The data corresponding to the ach_credit_transfer type."
    )
    amount = StripeDecimalCurrencyAmountField(
        null=True,
        blank=True,
        help_text=(
            "Amount (as decimal) associated with the ACH Credit Transfer. "
            "This is the amount your customer has sent for which the source will be chargeable once ready. "
        ),
    )
    currency = StripeCurrencyCodeField()

    # did not use CharField because no idea about possible max-length
    customer_data = JSONField(
        null=True,
        blank=True,
        help_text="Customer defined string used to initiate the ACH Credit Transfer.",
    )
    # source cannot be null but we are allowing this because the order of webhooks is not deterministic
    source = StripeForeignKey(
        "Source",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
    )
    status = StripeEnumField(
        enum=enums.SourceTransactionStatus,
        help_text="The status of the ACH Credit Transfer. Only `chargeable` sources can be used "
        "to create a charge.",
    )

    def __str__(self):
        return f"Source Transaction status={self.status}, source={self.source.id}"

    def get_stripe_dashboard_url(self) -> str:
        """Get the stripe dashboard url for this object."""
        if (
            not self.stripe_dashboard_item_name
            or not self.id
            or not self.source
            or not self.source.id
        ):
            return ""
        else:
            return f"{self._get_base_stripe_dashboard_url()}sources/{self.source.id}"

    @classmethod
    def api_list(cls, api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs):
        """
        Call the stripe API's list operation for this model.
        :param api_key: The api key to use for this request. \
            Defaults to djstripe_settings.STRIPE_SECRET_KEY.
        :type api_key: string
        See Stripe documentation for accepted kwargs for each object.
        :returns: an iterator over all items in the query
        """
        # Update kwargs with `expand` param
        kwargs = cls.get_expand_params(api_key, **kwargs)

        source = kwargs.pop("id", None)
        if not source:
            raise KeyError("Source Object ID is missing")

        return stripe.Source.list_source_transactions(
            source, api_key=api_key, **kwargs
        ).auto_paging_iter()

    def api_retrieve(self, api_key=None, stripe_account=None):
        """
        Call the stripe API's retrieve operation for this model.

        :param api_key: The api key to use for this request. \
            Defaults to djstripe_settings.STRIPE_SECRET_KEY.
        :type api_key: string
        :param stripe_account: The optional connected account \
            for which this request is being made.
        :type stripe_account: string
        """
        # Prefer passed in stripe_account if set.
        if not stripe_account:
            stripe_account = self._get_stripe_account_id(api_key)

        for source_trx in SourceTransaction.api_list(
            id=self.source.id, api_key=api_key, stripe_account=stripe_account
        ):
            if source_trx.id == self.id:
                return source_trx

Attributes

djstripe.models.payment_methods.SourceTransaction.ach_credit_transfer = JSONField(help_text='The data corresponding to the ach_credit_transfer type.') class-attribute instance-attribute
djstripe.models.payment_methods.SourceTransaction.amount = StripeDecimalCurrencyAmountField(null=True, blank=True, help_text='Amount (as decimal) associated with the ACH Credit Transfer. This is the amount your customer has sent for which the source will be chargeable once ready. ') class-attribute instance-attribute
djstripe.models.payment_methods.SourceTransaction.currency = StripeCurrencyCodeField() class-attribute instance-attribute
djstripe.models.payment_methods.SourceTransaction.customer_data = JSONField(null=True, blank=True, help_text='Customer defined string used to initiate the ACH Credit Transfer.') class-attribute instance-attribute
djstripe.models.payment_methods.SourceTransaction.description = None class-attribute instance-attribute
djstripe.models.payment_methods.SourceTransaction.metadata = None class-attribute instance-attribute
djstripe.models.payment_methods.SourceTransaction.source = StripeForeignKey('Source', on_delete=models.SET_NULL, null=True, blank=True) class-attribute instance-attribute
djstripe.models.payment_methods.SourceTransaction.status = StripeEnumField(enum=enums.SourceTransactionStatus, help_text='The status of the ACH Credit Transfer. Only `chargeable` sources can be used to create a charge.') class-attribute instance-attribute
djstripe.models.payment_methods.SourceTransaction.stripe_class = stripe.SourceTransaction class-attribute instance-attribute
djstripe.models.payment_methods.SourceTransaction.stripe_dashboard_item_name = 'source_transactions' class-attribute instance-attribute
djstripe.models.payment_methods.SourceTransaction.type = enums.SourceType.ach_credit_transfer class-attribute instance-attribute

Functions

djstripe.models.payment_methods.SourceTransaction.__str__()
Source code in djstripe/models/payment_methods.py
882
883
def __str__(self):
    return f"Source Transaction status={self.status}, source={self.source.id}"
djstripe.models.payment_methods.SourceTransaction.api_list(api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs) classmethod

Call the stripe API's list operation for this model. :param api_key: The api key to use for this request. Defaults to djstripe_settings.STRIPE_SECRET_KEY. :type api_key: string See Stripe documentation for accepted kwargs for each object. :returns: an iterator over all items in the query

Source code in djstripe/models/payment_methods.py
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
@classmethod
def api_list(cls, api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs):
    """
    Call the stripe API's list operation for this model.
    :param api_key: The api key to use for this request. \
        Defaults to djstripe_settings.STRIPE_SECRET_KEY.
    :type api_key: string
    See Stripe documentation for accepted kwargs for each object.
    :returns: an iterator over all items in the query
    """
    # Update kwargs with `expand` param
    kwargs = cls.get_expand_params(api_key, **kwargs)

    source = kwargs.pop("id", None)
    if not source:
        raise KeyError("Source Object ID is missing")

    return stripe.Source.list_source_transactions(
        source, api_key=api_key, **kwargs
    ).auto_paging_iter()
djstripe.models.payment_methods.SourceTransaction.api_retrieve(api_key=None, stripe_account=None)

Call the stripe API's retrieve operation for this model.

:param api_key: The api key to use for this request. Defaults to djstripe_settings.STRIPE_SECRET_KEY. :type api_key: string :param stripe_account: The optional connected account for which this request is being made. :type stripe_account: string

Source code in djstripe/models/payment_methods.py
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
def api_retrieve(self, api_key=None, stripe_account=None):
    """
    Call the stripe API's retrieve operation for this model.

    :param api_key: The api key to use for this request. \
        Defaults to djstripe_settings.STRIPE_SECRET_KEY.
    :type api_key: string
    :param stripe_account: The optional connected account \
        for which this request is being made.
    :type stripe_account: string
    """
    # Prefer passed in stripe_account if set.
    if not stripe_account:
        stripe_account = self._get_stripe_account_id(api_key)

    for source_trx in SourceTransaction.api_list(
        id=self.source.id, api_key=api_key, stripe_account=stripe_account
    ):
        if source_trx.id == self.id:
            return source_trx
djstripe.models.payment_methods.SourceTransaction.get_stripe_dashboard_url()

Get the stripe dashboard url for this object.

Source code in djstripe/models/payment_methods.py
885
886
887
888
889
890
891
892
893
894
895
def get_stripe_dashboard_url(self) -> str:
    """Get the stripe dashboard url for this object."""
    if (
        not self.stripe_dashboard_item_name
        or not self.id
        or not self.source
        or not self.source.id
    ):
        return ""
    else:
        return f"{self._get_base_stripe_dashboard_url()}sources/{self.source.id}"

Functions

Billing

Attributes

djstripe.models.billing.logger = logging.getLogger(__name__) module-attribute

Classes

djstripe.models.billing.BaseInvoice

Bases: StripeModel

The abstract base model shared by Invoice and UpcomingInvoice

Note: Most fields are defined on BaseInvoice so they're available to both models. ManyToManyFields are an exception, since UpcomingInvoice doesn't exist in the db.

Source code in djstripe/models/billing.py
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
class BaseInvoice(StripeModel):
    """
    The abstract base model shared by Invoice and UpcomingInvoice

    Note:
    Most fields are defined on BaseInvoice so they're available to both models.
    ManyToManyFields are an exception, since UpcomingInvoice doesn't exist in the db.
    """

    stripe_class = stripe.Invoice
    stripe_dashboard_item_name = "invoices"
    expand_fields = ["discounts", "lines.data.discounts"]

    account_country = models.CharField(
        max_length=2,
        default="",
        blank=True,
        help_text=(
            "The country of the business associated with this invoice, "
            "most often the business creating the invoice."
        ),
    )
    account_name = models.TextField(
        max_length=5000,
        blank=True,
        help_text=(
            "The public name of the business associated with this invoice, "
            "most often the business creating the invoice."
        ),
    )
    amount_due = StripeDecimalCurrencyAmountField(
        help_text=(
            "Final amount due (as decimal) at this time for this invoice. If the"
            " invoice's total is smaller than the minimum charge amount, for example,"
            " or if there is account credit that can be applied to the invoice, the"
            " amount_due may be 0. If there is a positive starting_balance for the"
            " invoice (the customer owes money), the amount_due will also take that"
            " into account. The charge that gets generated for the invoice will be for"
            " the amount specified in amount_due."
        )
    )
    amount_paid = StripeDecimalCurrencyAmountField(
        null=True,  # XXX: This is not nullable, but it's a new field
        help_text="The amount, (as decimal), that was paid.",
    )
    amount_remaining = StripeDecimalCurrencyAmountField(
        null=True,  # XXX: This is not nullable, but it's a new field
        help_text="The amount remaining, (as decimal), that is due.",
    )
    application_fee_amount = StripeDecimalCurrencyAmountField(
        null=True,
        blank=True,
        help_text=(
            "The fee (as decimal) that will be applied to the invoice and "
            "transferred to the application owner's "
            "Stripe account when the invoice is paid."
        ),
    )
    attempt_count = models.IntegerField(
        help_text=(
            "Number of payment attempts made for this invoice, from the perspective of"
            " the payment retry schedule. Any payment attempt counts as the first"
            " attempt, and subsequently only automatic retries increment the attempt"
            " count. In other words, manual payment attempts after the first attempt do"
            " not affect the retry schedule."
        )
    )
    attempted = models.BooleanField(
        default=False,
        help_text=(
            "Whether or not an attempt has been made to pay the invoice. "
            "An invoice is not attempted until 1 hour after the ``invoice.created`` "
            "webhook, for example, so you might not want to display that invoice as "
            "unpaid to your users."
        ),
    )
    auto_advance = models.BooleanField(
        null=True,
        help_text=(
            "Controls whether Stripe will perform automatic collection of the "
            "invoice. When false, the invoice's state will not automatically "
            "advance without an explicit action."
        ),
    )
    billing_reason = StripeEnumField(
        default="",
        blank=True,
        enum=enums.InvoiceBillingReason,
        help_text=(
            "Indicates the reason why the invoice was created. subscription_cycle"
            " indicates an invoice created by a subscription advancing into a new"
            " period. subscription_create indicates an invoice created due to creating"
            " a subscription. subscription_update indicates an invoice created due to"
            " updating a subscription. subscription is set for all old invoices to"
            " indicate either a change to a subscription or a period advancement."
            " manual is set for all invoices unrelated to a subscription (for example:"
            " created via the invoice editor). The upcoming value is reserved for"
            " simulated invoices per the upcoming invoice endpoint."
            " subscription_threshold indicates an invoice created due to a billing"
            " threshold being reached."
        ),
    )
    charge = models.OneToOneField(
        "Charge",
        on_delete=models.CASCADE,
        null=True,
        # we need to use the %(class)s placeholder to avoid related name
        # clashes between Invoice and UpcomingInvoice
        related_name="latest_%(class)s",
        help_text="The latest charge generated for this invoice, if any.",
    )
    collection_method = StripeEnumField(
        enum=enums.InvoiceCollectionMethod,
        null=True,
        help_text=(
            "When charging automatically, Stripe will attempt to pay this invoice "
            "using the default source attached to the customer. "
            "When sending an invoice, Stripe will email this invoice to the customer "
            "with payment instructions."
        ),
    )
    currency = StripeCurrencyCodeField()
    customer = StripeForeignKey(
        "Customer",
        on_delete=models.CASCADE,
        # we need to use the %(class)s placeholder to avoid related name
        # clashes between Invoice and UpcomingInvoice
        related_name="%(class)ss",
        help_text="The customer associated with this invoice.",
    )
    customer_address = JSONField(
        null=True,
        blank=True,
        help_text=(
            "The customer's address. Until the invoice is finalized, this field will"
            " equal customer.address. Once the invoice is finalized, this field will no"
            " longer be updated."
        ),
    )
    customer_email = models.TextField(
        max_length=5000,
        blank=True,
        help_text=(
            "The customer's email. Until the invoice is finalized, this field will"
            " equal customer.email. Once the invoice is finalized, this field will no"
            " longer be updated."
        ),
    )
    customer_name = models.TextField(
        max_length=5000,
        blank=True,
        help_text=(
            "The customer's name. Until the invoice is finalized, this field will equal"
            " customer.name. Once the invoice is finalized, this field will no longer"
            " be updated."
        ),
    )
    customer_phone = models.TextField(
        max_length=5000,
        blank=True,
        help_text=(
            "The customer's phone number. Until the invoice is finalized, "
            "this field will equal customer.phone. Once the invoice is finalized, "
            "this field will no longer be updated."
        ),
    )
    customer_shipping = JSONField(
        null=True,
        blank=True,
        help_text=(
            "The customer's shipping information. Until the invoice is "
            "finalized, this field will equal customer.shipping. Once the invoice is "
            "finalized, this field will no longer be updated."
        ),
    )
    customer_tax_exempt = StripeEnumField(
        enum=enums.CustomerTaxExempt,
        default="",
        help_text=(
            "The customer's tax exempt status. Until the invoice is finalized, "
            "this field will equal customer.tax_exempt. Once the invoice is "
            "finalized, this field will no longer be updated."
        ),
    )
    default_payment_method = StripeForeignKey(
        "PaymentMethod",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+",
        help_text=(
            "Default payment method for the invoice. It must belong to the "
            "customer associated with the invoice. If not set, defaults to the "
            "subscription's default payment method, if any, or to the default payment "
            "method in the customer's invoice settings."
        ),
    )
    # Note: default_tax_rates is handled in the subclasses since it's a
    # ManyToManyField, otherwise reverse accessors clash
    discount = JSONField(
        null=True,
        blank=True,
        help_text=(
            "Deprecated! Please use discounts instead. Describes the current discount"
            " applied to this subscription, if there is one. When billing, a discount"
            " applied to a subscription overrides a discount applied on a customer-wide"
            " basis."
        ),
    )
    discounts = JSONField(
        null=True,
        blank=True,
        help_text=(
            "The discounts applied to the invoice. Line item discounts are applied"
            " before invoice discounts."
        ),
    )
    due_date = StripeDateTimeField(
        null=True,
        blank=True,
        help_text=(
            "The date on which payment for this invoice is due. "
            "This value will be null for invoices where billing=charge_automatically."
        ),
    )
    ending_balance = StripeQuantumCurrencyAmountField(
        null=True,
        help_text=(
            "Ending customer balance (in cents) after attempting to pay invoice. "
            "If the invoice has not been attempted yet, this will be null."
        ),
    )
    footer = models.TextField(
        max_length=5000, blank=True, help_text="Footer displayed on the invoice."
    )
    hosted_invoice_url = models.TextField(
        max_length=799,
        default="",
        blank=True,
        help_text=(
            "The URL for the hosted invoice page, which allows customers to view "
            "and pay an invoice. If the invoice has not been frozen yet, "
            "this will be null."
        ),
    )
    invoice_pdf = models.TextField(
        max_length=799,
        default="",
        blank=True,
        help_text=(
            "The link to download the PDF for the invoice. "
            "If the invoice has not been frozen yet, this will be null."
        ),
    )
    # TODO: Implement "lines" (InvoiceLineItem related_field)
    next_payment_attempt = StripeDateTimeField(
        null=True,
        blank=True,
        help_text="The time at which payment will next be attempted.",
    )
    number = models.CharField(
        max_length=64,
        default="",
        blank=True,
        help_text=(
            "A unique, identifying string that appears on emails sent to the customer "
            "for this invoice. "
            "This starts with the customer's unique invoice_prefix if it is specified."
        ),
    )
    paid = models.BooleanField(
        default=False,
        help_text=(
            "Whether payment was successfully collected for this invoice. An invoice "
            "can be paid (most commonly) with a charge or with credit from the "
            "customer's account balance."
        ),
    )
    payment_intent = models.OneToOneField(
        "PaymentIntent",
        on_delete=models.CASCADE,
        null=True,
        help_text=(
            "The PaymentIntent associated with this invoice. "
            "The PaymentIntent is generated when the invoice is finalized, "
            "and can then be used to pay the invoice."
            "Note that voiding an invoice will cancel the PaymentIntent"
        ),
    )
    period_end = StripeDateTimeField(
        help_text=(
            "End of the usage period during which invoice items were "
            "added to this invoice."
        )
    )
    period_start = StripeDateTimeField(
        help_text=(
            "Start of the usage period during which invoice items were "
            "added to this invoice."
        )
    )
    post_payment_credit_notes_amount = StripeQuantumCurrencyAmountField(
        # This is not nullable, but it's a new field
        null=True,
        blank=True,
        help_text=(
            "Total amount (in cents) of all post-payment credit notes issued "
            "for this invoice."
        ),
    )
    pre_payment_credit_notes_amount = StripeQuantumCurrencyAmountField(
        # This is not nullable, but it's a new field
        null=True,
        blank=True,
        help_text=(
            "Total amount (in cents) of all pre-payment credit notes issued "
            "for this invoice."
        ),
    )
    receipt_number = models.CharField(
        max_length=64,
        null=True,
        blank=True,
        help_text=(
            "This is the transaction number that appears on email receipts "
            "sent for this invoice."
        ),
    )
    starting_balance = StripeQuantumCurrencyAmountField(
        help_text=(
            "Starting customer balance (in cents) before attempting to pay "
            "invoice. If the invoice has not been attempted yet, this will be the "
            "current customer balance."
        )
    )
    statement_descriptor = models.CharField(
        max_length=22,
        default="",
        blank=True,
        help_text=(
            "An arbitrary string to be displayed on your customer's credit card"
            " statement. The statement description may not include <>\"' characters,"
            " and will appear on your customer's statement in capital letters."
            " Non-ASCII characters are automatically stripped. While most banks display"
            " this information consistently, some may display it incorrectly or not at"
            " all."
        ),
    )
    status = StripeEnumField(
        default="",
        blank=True,
        enum=enums.InvoiceStatus,
        help_text=(
            "The status of the invoice, one of draft, open, paid, "
            "uncollectible, or void."
        ),
    )
    status_transitions = JSONField(null=True, blank=True)
    subscription = StripeForeignKey(
        "Subscription",
        null=True,
        # we need to use the %(class)s placeholder to avoid related name
        # clashes between Invoice and UpcomingInvoice
        related_name="%(class)ss",
        on_delete=models.SET_NULL,
        help_text="The subscription that this invoice was prepared for, if any.",
    )
    subscription_proration_date = StripeDateTimeField(
        null=True,
        blank=True,
        help_text=(
            "Only set for upcoming invoices that preview prorations. "
            "The time used to calculate prorations."
        ),
    )
    subtotal = StripeDecimalCurrencyAmountField(
        help_text=(
            "Total (as decimal) of all subscriptions, invoice items, "
            "and prorations on the invoice before any discount or tax is applied."
        )
    )
    tax = StripeDecimalCurrencyAmountField(
        null=True,
        blank=True,
        help_text=(
            "The amount (as decimal) of tax included in the total, calculated "
            "from ``tax_percent`` and the subtotal. If no "
            "``tax_percent`` is defined, this value will be null."
        ),
    )
    tax_percent = StripePercentField(
        null=True,
        blank=True,
        help_text=(
            "This percentage of the subtotal has been added to the total amount of the"
            " invoice, including invoice line items and discounts. This field is"
            " inherited from the subscription's ``tax_percent`` field, but can be"
            " changed before the invoice is paid. This field defaults to null."
        ),
    )
    threshold_reason = JSONField(
        null=True,
        blank=True,
        help_text=(
            "If billing_reason is set to subscription_threshold this returns "
            "more information on which threshold rules triggered the invoice."
        ),
    )
    total = StripeDecimalCurrencyAmountField("Total (as decimal) after discount.")
    webhooks_delivered_at = StripeDateTimeField(
        null=True,
        help_text=(
            "The time at which webhooks for this invoice were successfully delivered "
            "(if the invoice had no webhooks to deliver, this will match `date`). "
            "Invoice payment is delayed until webhooks are delivered, or until all "
            "webhook delivery attempts have been exhausted."
        ),
    )

    class Meta(StripeModel.Meta):
        abstract = True
        ordering = ["-created"]

    def __str__(self):
        invoice_number = self.number or self.receipt_number or self.id
        amount = get_friendly_currency_amount(self.amount_paid or 0, self.currency)
        return f"Invoice #{invoice_number} for {amount} ({self.status})"

    @classmethod
    def upcoming(
        cls,
        api_key=djstripe_settings.STRIPE_SECRET_KEY,
        customer=None,
        subscription=None,
        subscription_plan=None,
        **kwargs,
    ) -> Optional["UpcomingInvoice"]:
        """
        Gets the upcoming preview invoice (singular) for a customer.

        At any time, you can preview the upcoming
        invoice for a customer. This will show you all the charges that are
        pending, including subscription renewal charges, invoice item charges,
        etc. It will also show you any discount that is applicable to the
        customer. (Source: https://stripe.com/docs/api#upcoming_invoice)

        .. important:: Note that when you are viewing an upcoming invoice,
            you are simply viewing a preview.

        :param customer: The identifier of the customer whose upcoming invoice \
        you'd like to retrieve.
        :type customer: Customer or string (customer ID)
        :param coupon: The code of the coupon to apply.
        :type coupon: str
        :param subscription: The identifier of the subscription to retrieve an \
        invoice for.
        :type subscription: Subscription or string (subscription ID)
        :param subscription_plan: If set, the invoice returned will preview \
        updating the subscription given to this plan, or creating a new \
        subscription to this plan if no subscription is given.
        :type subscription_plan: Plan or string (plan ID)
        """

        # Convert Customer to id
        if customer is not None and isinstance(customer, StripeModel):
            customer = customer.id

        # Convert Subscription to id
        if subscription is not None and isinstance(subscription, StripeModel):
            subscription = subscription.id

        # Convert Plan to id
        if subscription_plan is not None and isinstance(subscription_plan, StripeModel):
            subscription_plan = subscription_plan.id

        try:
            upcoming_stripe_invoice = cls.stripe_class.upcoming(
                api_key=api_key,
                customer=customer,
                subscription=subscription,
                subscription_plan=subscription_plan,
                stripe_version=djstripe_settings.STRIPE_API_VERSION,
                **kwargs,
            )
        except InvalidRequestError as exc:
            if str(exc) != "Nothing to invoice for customer":
                raise
            return None

        # Workaround for "id" being missing (upcoming invoices don't persist).
        upcoming_stripe_invoice["id"] = "upcoming"

        return UpcomingInvoice._create_from_stripe_object(
            upcoming_stripe_invoice,
            save=False,
            api_key=api_key,
        )

    def retry(self, **kwargs):
        """Retry payment on this invoice if it isn't paid."""

        if self.status != enums.InvoiceStatus.paid and self.auto_advance:
            stripe_invoice = self.api_retrieve()
            updated_stripe_invoice = stripe_invoice.pay(
                **kwargs
            )  # pay() throws an exception if the charge is not successful.
            type(self).sync_from_stripe_data(
                updated_stripe_invoice, api_key=self.default_api_key
            )
            return True
        return False

    def _attach_objects_post_save_hook(
        self,
        cls,
        data,
        api_key=djstripe_settings.STRIPE_SECRET_KEY,
        pending_relations=None,
    ):
        super()._attach_objects_post_save_hook(
            cls, data, api_key=api_key, pending_relations=pending_relations
        )

        # LineItems need a saved invoice because they're associated via a
        # RelatedManager, so this must be done as part of the post save hook.
        cls._stripe_object_to_line_items(
            target_cls=LineItem, data=data, invoice=self, api_key=api_key
        )
        # sync every discount
        for discount in self.discounts:
            Discount.sync_from_stripe_data(discount, api_key=api_key)

    @property
    def plan(self) -> Optional["Plan"]:
        """Gets the associated plan for this invoice.

        In order to provide a consistent view of invoices, the plan object
        should be taken from the first invoice item that has one, rather than
        using the plan associated with the subscription.

        Subscriptions (and their associated plan) are updated by the customer
        and represent what is current, but invoice items are immutable within
        the invoice and stay static/unchanged.

        In other words, a plan retrieved from an invoice item will represent
        the plan as it was at the time an invoice was issued.  The plan
        retrieved from the subscription will be the currently active plan.

        :returns: The associated plan for the invoice.
        """

        for invoiceitem in self.invoiceitems.all():
            if invoiceitem.plan:
                return invoiceitem.plan

        if self.subscription:
            return self.subscription.plan

        return None

Attributes

djstripe.models.billing.BaseInvoice.account_country = models.CharField(max_length=2, default='', blank=True, help_text='The country of the business associated with this invoice, most often the business creating the invoice.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.account_name = models.TextField(max_length=5000, blank=True, help_text='The public name of the business associated with this invoice, most often the business creating the invoice.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.amount_due = StripeDecimalCurrencyAmountField(help_text="Final amount due (as decimal) at this time for this invoice. If the invoice's total is smaller than the minimum charge amount, for example, or if there is account credit that can be applied to the invoice, the amount_due may be 0. If there is a positive starting_balance for the invoice (the customer owes money), the amount_due will also take that into account. The charge that gets generated for the invoice will be for the amount specified in amount_due.") class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.amount_paid = StripeDecimalCurrencyAmountField(null=True, help_text='The amount, (as decimal), that was paid.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.amount_remaining = StripeDecimalCurrencyAmountField(null=True, help_text='The amount remaining, (as decimal), that is due.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.application_fee_amount = StripeDecimalCurrencyAmountField(null=True, blank=True, help_text="The fee (as decimal) that will be applied to the invoice and transferred to the application owner's Stripe account when the invoice is paid.") class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.attempt_count = models.IntegerField(help_text='Number of payment attempts made for this invoice, from the perspective of the payment retry schedule. Any payment attempt counts as the first attempt, and subsequently only automatic retries increment the attempt count. In other words, manual payment attempts after the first attempt do not affect the retry schedule.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.attempted = models.BooleanField(default=False, help_text='Whether or not an attempt has been made to pay the invoice. An invoice is not attempted until 1 hour after the ``invoice.created`` webhook, for example, so you might not want to display that invoice as unpaid to your users.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.auto_advance = models.BooleanField(null=True, help_text="Controls whether Stripe will perform automatic collection of the invoice. When false, the invoice's state will not automatically advance without an explicit action.") class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.billing_reason = StripeEnumField(default='', blank=True, enum=enums.InvoiceBillingReason, help_text='Indicates the reason why the invoice was created. subscription_cycle indicates an invoice created by a subscription advancing into a new period. subscription_create indicates an invoice created due to creating a subscription. subscription_update indicates an invoice created due to updating a subscription. subscription is set for all old invoices to indicate either a change to a subscription or a period advancement. manual is set for all invoices unrelated to a subscription (for example: created via the invoice editor). The upcoming value is reserved for simulated invoices per the upcoming invoice endpoint. subscription_threshold indicates an invoice created due to a billing threshold being reached.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.charge = models.OneToOneField('Charge', on_delete=models.CASCADE, null=True, related_name='latest_%(class)s', help_text='The latest charge generated for this invoice, if any.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.collection_method = StripeEnumField(enum=enums.InvoiceCollectionMethod, null=True, help_text='When charging automatically, Stripe will attempt to pay this invoice using the default source attached to the customer. When sending an invoice, Stripe will email this invoice to the customer with payment instructions.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.currency = StripeCurrencyCodeField() class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.customer = StripeForeignKey('Customer', on_delete=models.CASCADE, related_name='%(class)ss', help_text='The customer associated with this invoice.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.customer_address = JSONField(null=True, blank=True, help_text="The customer's address. Until the invoice is finalized, this field will equal customer.address. Once the invoice is finalized, this field will no longer be updated.") class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.customer_email = models.TextField(max_length=5000, blank=True, help_text="The customer's email. Until the invoice is finalized, this field will equal customer.email. Once the invoice is finalized, this field will no longer be updated.") class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.customer_name = models.TextField(max_length=5000, blank=True, help_text="The customer's name. Until the invoice is finalized, this field will equal customer.name. Once the invoice is finalized, this field will no longer be updated.") class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.customer_phone = models.TextField(max_length=5000, blank=True, help_text="The customer's phone number. Until the invoice is finalized, this field will equal customer.phone. Once the invoice is finalized, this field will no longer be updated.") class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.customer_shipping = JSONField(null=True, blank=True, help_text="The customer's shipping information. Until the invoice is finalized, this field will equal customer.shipping. Once the invoice is finalized, this field will no longer be updated.") class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.customer_tax_exempt = StripeEnumField(enum=enums.CustomerTaxExempt, default='', help_text="The customer's tax exempt status. Until the invoice is finalized, this field will equal customer.tax_exempt. Once the invoice is finalized, this field will no longer be updated.") class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.default_payment_method = StripeForeignKey('PaymentMethod', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', help_text="Default payment method for the invoice. It must belong to the customer associated with the invoice. If not set, defaults to the subscription's default payment method, if any, or to the default payment method in the customer's invoice settings.") class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.discount = JSONField(null=True, blank=True, help_text='Deprecated! Please use discounts instead. Describes the current discount applied to this subscription, if there is one. When billing, a discount applied to a subscription overrides a discount applied on a customer-wide basis.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.discounts = JSONField(null=True, blank=True, help_text='The discounts applied to the invoice. Line item discounts are applied before invoice discounts.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.due_date = StripeDateTimeField(null=True, blank=True, help_text='The date on which payment for this invoice is due. This value will be null for invoices where billing=charge_automatically.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.ending_balance = StripeQuantumCurrencyAmountField(null=True, help_text='Ending customer balance (in cents) after attempting to pay invoice. If the invoice has not been attempted yet, this will be null.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.expand_fields = ['discounts', 'lines.data.discounts'] class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.footer = models.TextField(max_length=5000, blank=True, help_text='Footer displayed on the invoice.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.hosted_invoice_url = models.TextField(max_length=799, default='', blank=True, help_text='The URL for the hosted invoice page, which allows customers to view and pay an invoice. If the invoice has not been frozen yet, this will be null.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.invoice_pdf = models.TextField(max_length=799, default='', blank=True, help_text='The link to download the PDF for the invoice. If the invoice has not been frozen yet, this will be null.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.next_payment_attempt = StripeDateTimeField(null=True, blank=True, help_text='The time at which payment will next be attempted.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.number = models.CharField(max_length=64, default='', blank=True, help_text="A unique, identifying string that appears on emails sent to the customer for this invoice. This starts with the customer's unique invoice_prefix if it is specified.") class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.paid = models.BooleanField(default=False, help_text="Whether payment was successfully collected for this invoice. An invoice can be paid (most commonly) with a charge or with credit from the customer's account balance.") class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.payment_intent = models.OneToOneField('PaymentIntent', on_delete=models.CASCADE, null=True, help_text='The PaymentIntent associated with this invoice. The PaymentIntent is generated when the invoice is finalized, and can then be used to pay the invoice.Note that voiding an invoice will cancel the PaymentIntent') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.period_end = StripeDateTimeField(help_text='End of the usage period during which invoice items were added to this invoice.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.period_start = StripeDateTimeField(help_text='Start of the usage period during which invoice items were added to this invoice.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.plan: Optional[Plan] property

Gets the associated plan for this invoice.

In order to provide a consistent view of invoices, the plan object should be taken from the first invoice item that has one, rather than using the plan associated with the subscription.

Subscriptions (and their associated plan) are updated by the customer and represent what is current, but invoice items are immutable within the invoice and stay static/unchanged.

In other words, a plan retrieved from an invoice item will represent the plan as it was at the time an invoice was issued. The plan retrieved from the subscription will be the currently active plan.

:returns: The associated plan for the invoice.

djstripe.models.billing.BaseInvoice.post_payment_credit_notes_amount = StripeQuantumCurrencyAmountField(null=True, blank=True, help_text='Total amount (in cents) of all post-payment credit notes issued for this invoice.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.pre_payment_credit_notes_amount = StripeQuantumCurrencyAmountField(null=True, blank=True, help_text='Total amount (in cents) of all pre-payment credit notes issued for this invoice.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.receipt_number = models.CharField(max_length=64, null=True, blank=True, help_text='This is the transaction number that appears on email receipts sent for this invoice.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.starting_balance = StripeQuantumCurrencyAmountField(help_text='Starting customer balance (in cents) before attempting to pay invoice. If the invoice has not been attempted yet, this will be the current customer balance.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.statement_descriptor = models.CharField(max_length=22, default='', blank=True, help_text='An arbitrary string to be displayed on your customer\'s credit card statement. The statement description may not include <>"\' characters, and will appear on your customer\'s statement in capital letters. Non-ASCII characters are automatically stripped. While most banks display this information consistently, some may display it incorrectly or not at all.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.status = StripeEnumField(default='', blank=True, enum=enums.InvoiceStatus, help_text='The status of the invoice, one of draft, open, paid, uncollectible, or void.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.status_transitions = JSONField(null=True, blank=True) class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.stripe_class = stripe.Invoice class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.stripe_dashboard_item_name = 'invoices' class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.subscription = StripeForeignKey('Subscription', null=True, related_name='%(class)ss', on_delete=models.SET_NULL, help_text='The subscription that this invoice was prepared for, if any.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.subscription_proration_date = StripeDateTimeField(null=True, blank=True, help_text='Only set for upcoming invoices that preview prorations. The time used to calculate prorations.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.subtotal = StripeDecimalCurrencyAmountField(help_text='Total (as decimal) of all subscriptions, invoice items, and prorations on the invoice before any discount or tax is applied.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.tax = StripeDecimalCurrencyAmountField(null=True, blank=True, help_text='The amount (as decimal) of tax included in the total, calculated from ``tax_percent`` and the subtotal. If no ``tax_percent`` is defined, this value will be null.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.tax_percent = StripePercentField(null=True, blank=True, help_text="This percentage of the subtotal has been added to the total amount of the invoice, including invoice line items and discounts. This field is inherited from the subscription's ``tax_percent`` field, but can be changed before the invoice is paid. This field defaults to null.") class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.threshold_reason = JSONField(null=True, blank=True, help_text='If billing_reason is set to subscription_threshold this returns more information on which threshold rules triggered the invoice.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.total = StripeDecimalCurrencyAmountField('Total (as decimal) after discount.') class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.webhooks_delivered_at = StripeDateTimeField(null=True, help_text='The time at which webhooks for this invoice were successfully delivered (if the invoice had no webhooks to deliver, this will match `date`). Invoice payment is delayed until webhooks are delivered, or until all webhook delivery attempts have been exhausted.') class-attribute instance-attribute

Classes

djstripe.models.billing.BaseInvoice.Meta

Bases: Meta

Source code in djstripe/models/billing.py
724
725
726
class Meta(StripeModel.Meta):
    abstract = True
    ordering = ["-created"]
Attributes
djstripe.models.billing.BaseInvoice.Meta.abstract = True class-attribute instance-attribute
djstripe.models.billing.BaseInvoice.Meta.ordering = ['-created'] class-attribute instance-attribute

Functions

djstripe.models.billing.BaseInvoice.__str__()
Source code in djstripe/models/billing.py
728
729
730
731
def __str__(self):
    invoice_number = self.number or self.receipt_number or self.id
    amount = get_friendly_currency_amount(self.amount_paid or 0, self.currency)
    return f"Invoice #{invoice_number} for {amount} ({self.status})"
djstripe.models.billing.BaseInvoice.retry(**kwargs)

Retry payment on this invoice if it isn't paid.

Source code in djstripe/models/billing.py
803
804
805
806
807
808
809
810
811
812
813
814
815
def retry(self, **kwargs):
    """Retry payment on this invoice if it isn't paid."""

    if self.status != enums.InvoiceStatus.paid and self.auto_advance:
        stripe_invoice = self.api_retrieve()
        updated_stripe_invoice = stripe_invoice.pay(
            **kwargs
        )  # pay() throws an exception if the charge is not successful.
        type(self).sync_from_stripe_data(
            updated_stripe_invoice, api_key=self.default_api_key
        )
        return True
    return False
djstripe.models.billing.BaseInvoice.upcoming(api_key=djstripe_settings.STRIPE_SECRET_KEY, customer=None, subscription=None, subscription_plan=None, **kwargs) classmethod

Gets the upcoming preview invoice (singular) for a customer.

At any time, you can preview the upcoming invoice for a customer. This will show you all the charges that are pending, including subscription renewal charges, invoice item charges, etc. It will also show you any discount that is applicable to the customer. (Source: https://stripe.com/docs/api#upcoming_invoice)

.. important:: Note that when you are viewing an upcoming invoice, you are simply viewing a preview.

:param customer: The identifier of the customer whose upcoming invoice you'd like to retrieve. :type customer: Customer or string (customer ID) :param coupon: The code of the coupon to apply. :type coupon: str :param subscription: The identifier of the subscription to retrieve an invoice for. :type subscription: Subscription or string (subscription ID) :param subscription_plan: If set, the invoice returned will preview updating the subscription given to this plan, or creating a new subscription to this plan if no subscription is given. :type subscription_plan: Plan or string (plan ID)

Source code in djstripe/models/billing.py
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
@classmethod
def upcoming(
    cls,
    api_key=djstripe_settings.STRIPE_SECRET_KEY,
    customer=None,
    subscription=None,
    subscription_plan=None,
    **kwargs,
) -> Optional["UpcomingInvoice"]:
    """
    Gets the upcoming preview invoice (singular) for a customer.

    At any time, you can preview the upcoming
    invoice for a customer. This will show you all the charges that are
    pending, including subscription renewal charges, invoice item charges,
    etc. It will also show you any discount that is applicable to the
    customer. (Source: https://stripe.com/docs/api#upcoming_invoice)

    .. important:: Note that when you are viewing an upcoming invoice,
        you are simply viewing a preview.

    :param customer: The identifier of the customer whose upcoming invoice \
    you'd like to retrieve.
    :type customer: Customer or string (customer ID)
    :param coupon: The code of the coupon to apply.
    :type coupon: str
    :param subscription: The identifier of the subscription to retrieve an \
    invoice for.
    :type subscription: Subscription or string (subscription ID)
    :param subscription_plan: If set, the invoice returned will preview \
    updating the subscription given to this plan, or creating a new \
    subscription to this plan if no subscription is given.
    :type subscription_plan: Plan or string (plan ID)
    """

    # Convert Customer to id
    if customer is not None and isinstance(customer, StripeModel):
        customer = customer.id

    # Convert Subscription to id
    if subscription is not None and isinstance(subscription, StripeModel):
        subscription = subscription.id

    # Convert Plan to id
    if subscription_plan is not None and isinstance(subscription_plan, StripeModel):
        subscription_plan = subscription_plan.id

    try:
        upcoming_stripe_invoice = cls.stripe_class.upcoming(
            api_key=api_key,
            customer=customer,
            subscription=subscription,
            subscription_plan=subscription_plan,
            stripe_version=djstripe_settings.STRIPE_API_VERSION,
            **kwargs,
        )
    except InvalidRequestError as exc:
        if str(exc) != "Nothing to invoice for customer":
            raise
        return None

    # Workaround for "id" being missing (upcoming invoices don't persist).
    upcoming_stripe_invoice["id"] = "upcoming"

    return UpcomingInvoice._create_from_stripe_object(
        upcoming_stripe_invoice,
        save=False,
        api_key=api_key,
    )

djstripe.models.billing.Coupon

Bases: StripeModel

A coupon contains information about a percent-off or amount-off discount you might want to apply to a customer. Coupons may be applied to invoices or orders. Coupons do not work with conventional one-off charges.

Stripe documentation: https://stripe.com/docs/api/coupons?lang=python

Source code in djstripe/models/billing.py
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
class Coupon(StripeModel):
    """
    A coupon contains information about a percent-off or amount-off discount you might want to apply to a customer.
    Coupons may be applied to invoices or orders.
    Coupons do not work with conventional one-off charges.

    Stripe documentation: https://stripe.com/docs/api/coupons?lang=python
    """

    stripe_class = stripe.Coupon
    expand_fields = ["applies_to"]
    stripe_dashboard_item_name = "coupons"

    id = StripeIdField(max_length=500)
    applies_to = JSONField(
        null=True,
        blank=True,
        help_text="Contains information about what this coupon applies to.",
    )
    amount_off = StripeDecimalCurrencyAmountField(
        null=True,
        blank=True,
        help_text=(
            "Amount (as decimal) that will be taken off the subtotal of any "
            "invoices for this customer."
        ),
    )
    currency = StripeCurrencyCodeField(null=True, blank=True)
    duration = StripeEnumField(
        enum=enums.CouponDuration,
        help_text=(
            "Describes how long a customer who applies this coupon "
            "will get the discount."
        ),
        default=enums.CouponDuration.once,
    )
    duration_in_months = models.PositiveIntegerField(
        null=True,
        blank=True,
        help_text=(
            "If `duration` is `repeating`, the number of months the coupon applies."
        ),
    )
    max_redemptions = models.PositiveIntegerField(
        null=True,
        blank=True,
        help_text=(
            "Maximum number of times this coupon can be redeemed, in total, "
            "before it is no longer valid."
        ),
    )
    name = models.TextField(
        max_length=5000,
        default="",
        blank=True,
        help_text=(
            "Name of the coupon displayed to customers on for instance invoices "
            "or receipts."
        ),
    )
    percent_off = StripePercentField(
        null=True,
        blank=True,
        help_text=(
            "Percent that will be taken off the subtotal of any invoices for "
            "this customer for the duration of the coupon. "
            "For example, a coupon with percent_off of 50 will make a "
            "$100 invoice $50 instead."
        ),
    )
    redeem_by = StripeDateTimeField(
        null=True,
        blank=True,
        help_text=(
            "Date after which the coupon can no longer be redeemed. "
            "Max 5 years in the future."
        ),
    )
    times_redeemed = models.PositiveIntegerField(
        editable=False,
        default=0,
        help_text="Number of times this coupon has been applied to a customer.",
    )
    # valid = models.BooleanField(editable=False)

    class Meta(StripeModel.Meta):
        unique_together = ("id", "livemode")

    def __str__(self):
        if self.name:
            return self.name
        return self.human_readable

    @property
    def human_readable_amount(self):
        if self.percent_off:
            amount = f"{self.percent_off}%"
        elif self.currency:
            amount = get_friendly_currency_amount(self.amount_off or 0, self.currency)
        else:
            amount = "(invalid amount)"
        return f"{amount} off"

    @property
    def human_readable(self):
        if self.duration == enums.CouponDuration.repeating:
            if self.duration_in_months == 1:
                duration = "for 1 month"
            else:
                duration = f"for {self.duration_in_months} months"
        else:
            duration = self.duration
        return f"{self.human_readable_amount} {duration}"

Attributes

djstripe.models.billing.Coupon.amount_off = StripeDecimalCurrencyAmountField(null=True, blank=True, help_text='Amount (as decimal) that will be taken off the subtotal of any invoices for this customer.') class-attribute instance-attribute
djstripe.models.billing.Coupon.applies_to = JSONField(null=True, blank=True, help_text='Contains information about what this coupon applies to.') class-attribute instance-attribute
djstripe.models.billing.Coupon.currency = StripeCurrencyCodeField(null=True, blank=True) class-attribute instance-attribute
djstripe.models.billing.Coupon.duration = StripeEnumField(enum=enums.CouponDuration, help_text='Describes how long a customer who applies this coupon will get the discount.', default=enums.CouponDuration.once) class-attribute instance-attribute
djstripe.models.billing.Coupon.duration_in_months = models.PositiveIntegerField(null=True, blank=True, help_text='If `duration` is `repeating`, the number of months the coupon applies.') class-attribute instance-attribute
djstripe.models.billing.Coupon.expand_fields = ['applies_to'] class-attribute instance-attribute
djstripe.models.billing.Coupon.human_readable property
djstripe.models.billing.Coupon.human_readable_amount property
djstripe.models.billing.Coupon.id = StripeIdField(max_length=500) class-attribute instance-attribute
djstripe.models.billing.Coupon.max_redemptions = models.PositiveIntegerField(null=True, blank=True, help_text='Maximum number of times this coupon can be redeemed, in total, before it is no longer valid.') class-attribute instance-attribute
djstripe.models.billing.Coupon.name = models.TextField(max_length=5000, default='', blank=True, help_text='Name of the coupon displayed to customers on for instance invoices or receipts.') class-attribute instance-attribute
djstripe.models.billing.Coupon.percent_off = StripePercentField(null=True, blank=True, help_text='Percent that will be taken off the subtotal of any invoices for this customer for the duration of the coupon. For example, a coupon with percent_off of 50 will make a $100 invoice $50 instead.') class-attribute instance-attribute
djstripe.models.billing.Coupon.redeem_by = StripeDateTimeField(null=True, blank=True, help_text='Date after which the coupon can no longer be redeemed. Max 5 years in the future.') class-attribute instance-attribute
djstripe.models.billing.Coupon.stripe_class = stripe.Coupon class-attribute instance-attribute
djstripe.models.billing.Coupon.stripe_dashboard_item_name = 'coupons' class-attribute instance-attribute
djstripe.models.billing.Coupon.times_redeemed = models.PositiveIntegerField(editable=False, default=0, help_text='Number of times this coupon has been applied to a customer.') class-attribute instance-attribute

Classes

djstripe.models.billing.Coupon.Meta

Bases: Meta

Source code in djstripe/models/billing.py
179
180
class Meta(StripeModel.Meta):
    unique_together = ("id", "livemode")
Attributes
djstripe.models.billing.Coupon.Meta.unique_together = ('id', 'livemode') class-attribute instance-attribute

Functions

djstripe.models.billing.Coupon.__str__()
Source code in djstripe/models/billing.py
182
183
184
185
def __str__(self):
    if self.name:
        return self.name
    return self.human_readable

djstripe.models.billing.Discount

Bases: StripeModel

A discount represents the actual application of a coupon or promotion code. It contains information about when the discount began, when it will end, and what it is applied to.

Stripe documentation: https://stripe.com/docs/api/discounts

Source code in djstripe/models/billing.py
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
class Discount(StripeModel):
    """
    A discount represents the actual application of a coupon or promotion code.
    It contains information about when the discount began,
    when it will end, and what it is applied to.

    Stripe documentation: https://stripe.com/docs/api/discounts
    """

    expand_fields = ["customer"]
    stripe_class = None

    checkout_session = StripeForeignKey(
        "Session",
        null=True,
        blank=True,
        on_delete=models.CASCADE,
        help_text=(
            "The Checkout session that this coupon is applied to, if it is applied to a"
            " particular session in payment mode. Will not be present for subscription"
            " mode."
        ),
    )
    coupon = JSONField(
        null=True,
        blank=True,
        help_text="Hash describing the coupon applied to create this discount.",
    )
    customer = StripeForeignKey(
        "Customer",
        null=True,
        blank=True,
        on_delete=models.CASCADE,
        help_text="The ID of the customer associated with this discount.",
        related_name="customer_discounts",
    )
    end = StripeDateTimeField(
        null=True,
        blank=True,
        help_text=(
            "If the coupon has a duration of repeating, the date that this discount"
            " will end. If the coupon has a duration of once or forever, this attribute"
            " will be null."
        ),
    )
    invoice = StripeForeignKey(
        "Invoice",
        null=True,
        blank=True,
        on_delete=models.CASCADE,
        help_text=(
            "The invoice that the discount’s coupon was applied to, if it was applied"
            " directly to a particular invoice."
        ),
        related_name="invoice_discounts",
    )
    invoice_item = InvoiceOrLineItemForeignKey(
        null=True,
        blank=True,
        on_delete=models.CASCADE,
        help_text=(
            "The invoice item id (or invoice line item id for invoice line items of"
            " type=‘subscription’) that the discount’s coupon was applied to, if it was"
            " applied directly to a particular invoice item or invoice line item."
        ),
    )
    promotion_code = models.CharField(
        max_length=255,
        blank=True,
        help_text="The promotion code applied to create this discount.",
    )
    start = StripeDateTimeField(
        null=True,
        blank=True,
        help_text="Date that the coupon was applied.",
    )
    subscription = StripeForeignKey(
        "subscription",
        null=True,
        blank=True,
        on_delete=models.CASCADE,
        help_text=(
            "The subscription that this coupon is applied to, if it is applied to a"
            " particular subscription."
        ),
        related_name="subscription_discounts",
    )

    @classmethod
    def is_valid_object(cls, data):
        """
        Returns whether the data is a valid object for the class
        """
        return "object" in data and data["object"] == "discount"

Attributes

djstripe.models.billing.Discount.checkout_session = StripeForeignKey('Session', null=True, blank=True, on_delete=models.CASCADE, help_text='The Checkout session that this coupon is applied to, if it is applied to a particular session in payment mode. Will not be present for subscription mode.') class-attribute instance-attribute
djstripe.models.billing.Discount.coupon = JSONField(null=True, blank=True, help_text='Hash describing the coupon applied to create this discount.') class-attribute instance-attribute
djstripe.models.billing.Discount.customer = StripeForeignKey('Customer', null=True, blank=True, on_delete=models.CASCADE, help_text='The ID of the customer associated with this discount.', related_name='customer_discounts') class-attribute instance-attribute
djstripe.models.billing.Discount.end = StripeDateTimeField(null=True, blank=True, help_text='If the coupon has a duration of repeating, the date that this discount will end. If the coupon has a duration of once or forever, this attribute will be null.') class-attribute instance-attribute
djstripe.models.billing.Discount.expand_fields = ['customer'] class-attribute instance-attribute
djstripe.models.billing.Discount.invoice = StripeForeignKey('Invoice', null=True, blank=True, on_delete=models.CASCADE, help_text='The invoice that the discount’s coupon was applied to, if it was applied directly to a particular invoice.', related_name='invoice_discounts') class-attribute instance-attribute
djstripe.models.billing.Discount.invoice_item = InvoiceOrLineItemForeignKey(null=True, blank=True, on_delete=models.CASCADE, help_text='The invoice item id (or invoice line item id for invoice line items of type=‘subscription’) that the discount’s coupon was applied to, if it was applied directly to a particular invoice item or invoice line item.') class-attribute instance-attribute
djstripe.models.billing.Discount.promotion_code = models.CharField(max_length=255, blank=True, help_text='The promotion code applied to create this discount.') class-attribute instance-attribute
djstripe.models.billing.Discount.start = StripeDateTimeField(null=True, blank=True, help_text='Date that the coupon was applied.') class-attribute instance-attribute
djstripe.models.billing.Discount.stripe_class = None class-attribute instance-attribute
djstripe.models.billing.Discount.subscription = StripeForeignKey('subscription', null=True, blank=True, on_delete=models.CASCADE, help_text='The subscription that this coupon is applied to, if it is applied to a particular subscription.', related_name='subscription_discounts') class-attribute instance-attribute

Functions

djstripe.models.billing.Discount.is_valid_object(data) classmethod

Returns whether the data is a valid object for the class

Source code in djstripe/models/billing.py
297
298
299
300
301
302
@classmethod
def is_valid_object(cls, data):
    """
    Returns whether the data is a valid object for the class
    """
    return "object" in data and data["object"] == "discount"

djstripe.models.billing.DjstripeInvoiceTotalTaxAmount

Bases: Model

An internal model that holds the value of elements of Invoice.total_tax_amounts

Note that this is named with the prefix Djstripe to avoid potential collision with a Stripe API object name.

Source code in djstripe/models/billing.py
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
class DjstripeInvoiceTotalTaxAmount(models.Model):
    """
    An internal model that holds the value of elements of Invoice.total_tax_amounts

    Note that this is named with the prefix Djstripe to avoid potential
    collision with a Stripe API object name.
    """

    invoice = StripeForeignKey(
        "Invoice", on_delete=models.CASCADE, related_name="total_tax_amounts"
    )

    amount = StripeQuantumCurrencyAmountField(
        help_text="The amount, in cents, of the tax."
    )
    inclusive = models.BooleanField(
        help_text="Whether this tax amount is inclusive or exclusive."
    )
    tax_rate = StripeForeignKey(
        "TaxRate",
        on_delete=models.CASCADE,
        help_text="The tax rate that was applied to get this tax amount.",
    )

    class Meta:
        unique_together = ["invoice", "tax_rate"]

Attributes

djstripe.models.billing.DjstripeInvoiceTotalTaxAmount.amount = StripeQuantumCurrencyAmountField(help_text='The amount, in cents, of the tax.') class-attribute instance-attribute
djstripe.models.billing.DjstripeInvoiceTotalTaxAmount.inclusive = models.BooleanField(help_text='Whether this tax amount is inclusive or exclusive.') class-attribute instance-attribute
djstripe.models.billing.DjstripeInvoiceTotalTaxAmount.invoice = StripeForeignKey('Invoice', on_delete=models.CASCADE, related_name='total_tax_amounts') class-attribute instance-attribute
djstripe.models.billing.DjstripeInvoiceTotalTaxAmount.tax_rate = StripeForeignKey('TaxRate', on_delete=models.CASCADE, help_text='The tax rate that was applied to get this tax amount.') class-attribute instance-attribute

Classes

djstripe.models.billing.DjstripeInvoiceTotalTaxAmount.Meta
Source code in djstripe/models/billing.py
61
62
class Meta:
    unique_together = ["invoice", "tax_rate"]
Attributes
djstripe.models.billing.DjstripeInvoiceTotalTaxAmount.Meta.unique_together = ['invoice', 'tax_rate'] class-attribute instance-attribute

djstripe.models.billing.DjstripeUpcomingInvoiceTotalTaxAmount

Bases: Model

As per DjstripeInvoiceTotalTaxAmount, except for UpcomingInvoice

Source code in djstripe/models/billing.py
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
class DjstripeUpcomingInvoiceTotalTaxAmount(models.Model):
    """
    As per DjstripeInvoiceTotalTaxAmount, except for UpcomingInvoice
    """

    invoice = models.ForeignKey(
        # Don't define related_name since property is defined in UpcomingInvoice
        "UpcomingInvoice",
        on_delete=models.CASCADE,
        related_name="+",
    )

    amount = StripeQuantumCurrencyAmountField(
        help_text="The amount, in cents, of the tax."
    )
    inclusive = models.BooleanField(
        help_text="Whether this tax amount is inclusive or exclusive."
    )
    tax_rate = StripeForeignKey(
        "TaxRate",
        on_delete=models.CASCADE,
        help_text="The tax rate that was applied to get this tax amount.",
    )

    class Meta:
        unique_together = ["invoice", "tax_rate"]

Attributes

djstripe.models.billing.DjstripeUpcomingInvoiceTotalTaxAmount.amount = StripeQuantumCurrencyAmountField(help_text='The amount, in cents, of the tax.') class-attribute instance-attribute
djstripe.models.billing.DjstripeUpcomingInvoiceTotalTaxAmount.inclusive = models.BooleanField(help_text='Whether this tax amount is inclusive or exclusive.') class-attribute instance-attribute
djstripe.models.billing.DjstripeUpcomingInvoiceTotalTaxAmount.invoice = models.ForeignKey('UpcomingInvoice', on_delete=models.CASCADE, related_name='+') class-attribute instance-attribute
djstripe.models.billing.DjstripeUpcomingInvoiceTotalTaxAmount.tax_rate = StripeForeignKey('TaxRate', on_delete=models.CASCADE, help_text='The tax rate that was applied to get this tax amount.') class-attribute instance-attribute

Classes

djstripe.models.billing.DjstripeUpcomingInvoiceTotalTaxAmount.Meta
Source code in djstripe/models/billing.py
90
91
class Meta:
    unique_together = ["invoice", "tax_rate"]
Attributes
djstripe.models.billing.DjstripeUpcomingInvoiceTotalTaxAmount.Meta.unique_together = ['invoice', 'tax_rate'] class-attribute instance-attribute

djstripe.models.billing.Invoice

Bases: BaseInvoice

Invoices are statements of what a customer owes for a particular billing period, including subscriptions, invoice items, and any automatic proration adjustments if necessary.

Once an invoice is created, payment is automatically attempted. Note that the payment, while automatic, does not happen exactly at the time of invoice creation. If you have configured webhooks, the invoice will wait until one hour after the last webhook is successfully sent (or the last webhook times out after failing).

Any customer credit on the account is applied before determining how much is due for that invoice (the amount that will be actually charged). If the amount due for the invoice is less than 50 cents (the minimum for a charge), we add the amount to the customer's running account balance to be added to the next invoice. If this amount is negative, it will act as a credit to offset the next invoice. Note that the customer account balance does not include unpaid invoices; it only includes balances that need to be taken into account when calculating the amount due for the next invoice.

Stripe documentation: https://stripe.com/docs/api?lang=python#invoices

Source code in djstripe/models/billing.py
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
class Invoice(BaseInvoice):
    """
    Invoices are statements of what a customer owes for a particular billing
    period, including subscriptions, invoice items, and any automatic proration
    adjustments if necessary.

    Once an invoice is created, payment is automatically attempted. Note that
    the payment, while automatic, does not happen exactly at the time of invoice
    creation. If you have configured webhooks, the invoice will wait until one
    hour after the last webhook is successfully sent (or the last webhook times
    out after failing).

    Any customer credit on the account is applied before determining how much is
    due for that invoice (the amount that will be actually charged).
    If the amount due for the invoice is less than 50 cents (the minimum for a
    charge), we add the amount to the customer's running account balance to be
    added to the next invoice. If this amount is negative, it will act as a
    credit to offset the next invoice. Note that the customer account balance
    does not include unpaid invoices; it only includes balances that need to be
    taken into account when calculating the amount due for the next invoice.

    Stripe documentation: https://stripe.com/docs/api?lang=python#invoices
    """

    default_source = PaymentMethodForeignKey(
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="invoices",
        help_text=(
            "The default payment source for the invoice. "
            "It must belong to the customer associated with the invoice and be "
            "in a chargeable state. If not set, defaults to the subscription's "
            "default source, if any, or to the customer's default source."
        ),
    )

    # Note:
    # Most fields are defined on BaseInvoice so they're shared with UpcomingInvoice.
    # ManyToManyFields are an exception, since UpcomingInvoice doesn't exist in the db.
    default_tax_rates = models.ManyToManyField(
        "TaxRate",
        # explicitly specify the joining table name as though the joining model
        # was defined with through="DjstripeInvoiceDefaultTaxRate"
        db_table="djstripe_djstripeinvoicedefaulttaxrate",
        related_name="+",
        blank=True,
        help_text="The tax rates applied to this invoice, if any.",
    )

    def _attach_objects_post_save_hook(
        self,
        cls,
        data,
        api_key=djstripe_settings.STRIPE_SECRET_KEY,
        pending_relations=None,
    ):
        super()._attach_objects_post_save_hook(
            cls, data, api_key=api_key, pending_relations=pending_relations
        )

        self.default_tax_rates.set(
            cls._stripe_object_to_default_tax_rates(
                target_cls=TaxRate, data=data, api_key=api_key
            )
        )

        cls._stripe_object_set_total_tax_amounts(
            target_cls=DjstripeInvoiceTotalTaxAmount,
            data=data,
            instance=self,
            api_key=api_key,
        )

Attributes

djstripe.models.billing.Invoice.default_source = PaymentMethodForeignKey(on_delete=models.SET_NULL, null=True, blank=True, related_name='invoices', help_text="The default payment source for the invoice. It must belong to the customer associated with the invoice and be in a chargeable state. If not set, defaults to the subscription's default source, if any, or to the customer's default source.") class-attribute instance-attribute
djstripe.models.billing.Invoice.default_tax_rates = models.ManyToManyField('TaxRate', db_table='djstripe_djstripeinvoicedefaulttaxrate', related_name='+', blank=True, help_text='The tax rates applied to this invoice, if any.') class-attribute instance-attribute

djstripe.models.billing.InvoiceItem

Bases: StripeModel

Sometimes you want to add a charge or credit to a customer but only actually charge the customer's card at the end of a regular billing cycle. This is useful for combining several charges to minimize per-transaction fees or having Stripe tabulate your usage-based billing totals.

Stripe documentation: https://stripe.com/docs/api?lang=python#invoiceitems

Source code in djstripe/models/billing.py
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
class InvoiceItem(StripeModel):
    """
    Sometimes you want to add a charge or credit to a customer but only actually
    charge the customer's card at the end of a regular billing cycle.
    This is useful for combining several charges to minimize per-transaction fees
    or having Stripe tabulate your usage-based billing totals.

    Stripe documentation: https://stripe.com/docs/api?lang=python#invoiceitems
    """

    stripe_class = stripe.InvoiceItem
    expand_fields = ["discounts"]

    amount = StripeDecimalCurrencyAmountField(help_text="Amount invoiced (as decimal).")
    currency = StripeCurrencyCodeField()
    customer = StripeForeignKey(
        "Customer",
        on_delete=models.CASCADE,
        related_name="invoiceitems",
        help_text="The customer associated with this invoiceitem.",
    )
    date = StripeDateTimeField(help_text="The date on the invoiceitem.")
    discountable = models.BooleanField(
        default=False,
        help_text=(
            "If True, discounts will apply to this invoice item. "
            "Always False for prorations."
        ),
    )
    discounts = JSONField(
        null=True,
        blank=True,
        help_text=(
            "The discounts which apply to the invoice item. Item discounts are applied"
            " before invoice discounts."
        ),
    )
    invoice = StripeForeignKey(
        "Invoice",
        on_delete=models.CASCADE,
        null=True,
        related_name="invoiceitems",
        help_text="The invoice to which this invoiceitem is attached.",
    )
    period = JSONField()
    period_end = StripeDateTimeField(
        help_text="Might be the date when this invoiceitem's invoice was sent."
    )
    period_start = StripeDateTimeField(
        help_text="Might be the date when this invoiceitem was added to the invoice"
    )
    plan = models.ForeignKey(
        "Plan",
        null=True,
        on_delete=models.SET_NULL,
        help_text=(
            "If the invoice item is a proration, the plan of the subscription "
            "for which the proration was computed."
        ),
    )
    price = models.ForeignKey(
        "Price",
        null=True,
        related_name="invoiceitems",
        on_delete=models.SET_NULL,
        help_text=(
            "If the invoice item is a proration, the price of the subscription "
            "for which the proration was computed."
        ),
    )
    proration = models.BooleanField(
        default=False,
        help_text=(
            "Whether or not the invoice item was created automatically as a "
            "proration adjustment when the customer switched plans."
        ),
    )
    quantity = models.IntegerField(
        null=True,
        blank=True,
        help_text=(
            "If the invoice item is a proration, the quantity of the "
            "subscription for which the proration was computed."
        ),
    )
    subscription = StripeForeignKey(
        "Subscription",
        null=True,
        related_name="invoiceitems",
        on_delete=models.SET_NULL,
        help_text=(
            "The subscription that this invoice item has been created for, if any."
        ),
    )
    # XXX: subscription_item
    tax_rates = models.ManyToManyField(
        "TaxRate",
        # explicitly specify the joining table name as though the joining model
        # was defined with through="DjstripeInvoiceItemTaxRate"
        db_table="djstripe_djstripeinvoiceitemtaxrate",
        related_name="+",
        blank=True,
        help_text=(
            "The tax rates which apply to this invoice item. When set, "
            "the default_tax_rates on the invoice do not apply to this "
            "invoice item."
        ),
    )
    unit_amount = StripeQuantumCurrencyAmountField(
        null=True,
        blank=True,
        help_text="Unit amount (in the `currency` specified) of the invoice item.",
    )
    unit_amount_decimal = StripeDecimalCurrencyAmountField(
        null=True,
        blank=True,
        max_digits=19,
        decimal_places=12,
        help_text=(
            "Same as `unit_amount`, but contains a decimal value with "
            "at most 12 decimal places."
        ),
    )

    @classmethod
    def _manipulate_stripe_object_hook(cls, data):
        data["period_start"] = data["period"]["start"]
        data["period_end"] = data["period"]["end"]

        return data

    def _attach_objects_post_save_hook(
        self,
        cls,
        data,
        api_key=djstripe_settings.STRIPE_SECRET_KEY,
        pending_relations=None,
    ):
        super()._attach_objects_post_save_hook(
            cls, data, api_key=api_key, pending_relations=pending_relations
        )

        if self.pk:
            # only call .set() on saved instance (ie don't on items of UpcomingInvoice)
            self.tax_rates.set(
                cls._stripe_object_to_tax_rates(
                    target_cls=TaxRate, data=data, api_key=api_key
                )
            )

        # sync every discount
        for discount in self.discounts:
            Discount.sync_from_stripe_data(discount, api_key=api_key)

    def __str__(self):
        return self.description

    def get_stripe_dashboard_url(self):
        return self.invoice.get_stripe_dashboard_url()

    def api_retrieve(self, *args, **kwargs):
        if "-il_" in self.id:
            warnings.warn(
                f"Attempting to retrieve InvoiceItem with id={self.id!r}"
                " will most likely fail. "
                "Run manage.py djstripe_update_invoiceitem_ids if this is a problem."
            )

        return super().api_retrieve(*args, **kwargs)

Attributes

djstripe.models.billing.InvoiceItem.amount = StripeDecimalCurrencyAmountField(help_text='Amount invoiced (as decimal).') class-attribute instance-attribute
djstripe.models.billing.InvoiceItem.currency = StripeCurrencyCodeField() class-attribute instance-attribute
djstripe.models.billing.InvoiceItem.customer = StripeForeignKey('Customer', on_delete=models.CASCADE, related_name='invoiceitems', help_text='The customer associated with this invoiceitem.') class-attribute instance-attribute
djstripe.models.billing.InvoiceItem.date = StripeDateTimeField(help_text='The date on the invoiceitem.') class-attribute instance-attribute
djstripe.models.billing.InvoiceItem.discountable = models.BooleanField(default=False, help_text='If True, discounts will apply to this invoice item. Always False for prorations.') class-attribute instance-attribute
djstripe.models.billing.InvoiceItem.discounts = JSONField(null=True, blank=True, help_text='The discounts which apply to the invoice item. Item discounts are applied before invoice discounts.') class-attribute instance-attribute
djstripe.models.billing.InvoiceItem.expand_fields = ['discounts'] class-attribute instance-attribute
djstripe.models.billing.InvoiceItem.invoice = StripeForeignKey('Invoice', on_delete=models.CASCADE, null=True, related_name='invoiceitems', help_text='The invoice to which this invoiceitem is attached.') class-attribute instance-attribute
djstripe.models.billing.InvoiceItem.period = JSONField() class-attribute instance-attribute
djstripe.models.billing.InvoiceItem.period_end = StripeDateTimeField(help_text="Might be the date when this invoiceitem's invoice was sent.") class-attribute instance-attribute
djstripe.models.billing.InvoiceItem.period_start = StripeDateTimeField(help_text='Might be the date when this invoiceitem was added to the invoice') class-attribute instance-attribute
djstripe.models.billing.InvoiceItem.plan = models.ForeignKey('Plan', null=True, on_delete=models.SET_NULL, help_text='If the invoice item is a proration, the plan of the subscription for which the proration was computed.') class-attribute instance-attribute
djstripe.models.billing.InvoiceItem.price = models.ForeignKey('Price', null=True, related_name='invoiceitems', on_delete=models.SET_NULL, help_text='If the invoice item is a proration, the price of the subscription for which the proration was computed.') class-attribute instance-attribute
djstripe.models.billing.InvoiceItem.proration = models.BooleanField(default=False, help_text='Whether or not the invoice item was created automatically as a proration adjustment when the customer switched plans.') class-attribute instance-attribute
djstripe.models.billing.InvoiceItem.quantity = models.IntegerField(null=True, blank=True, help_text='If the invoice item is a proration, the quantity of the subscription for which the proration was computed.') class-attribute instance-attribute
djstripe.models.billing.InvoiceItem.stripe_class = stripe.InvoiceItem class-attribute instance-attribute
djstripe.models.billing.InvoiceItem.subscription = StripeForeignKey('Subscription', null=True, related_name='invoiceitems', on_delete=models.SET_NULL, help_text='The subscription that this invoice item has been created for, if any.') class-attribute instance-attribute
djstripe.models.billing.InvoiceItem.tax_rates = models.ManyToManyField('TaxRate', db_table='djstripe_djstripeinvoiceitemtaxrate', related_name='+', blank=True, help_text='The tax rates which apply to this invoice item. When set, the default_tax_rates on the invoice do not apply to this invoice item.') class-attribute instance-attribute
djstripe.models.billing.InvoiceItem.unit_amount = StripeQuantumCurrencyAmountField(null=True, blank=True, help_text='Unit amount (in the `currency` specified) of the invoice item.') class-attribute instance-attribute
djstripe.models.billing.InvoiceItem.unit_amount_decimal = StripeDecimalCurrencyAmountField(null=True, blank=True, max_digits=19, decimal_places=12, help_text='Same as `unit_amount`, but contains a decimal value with at most 12 decimal places.') class-attribute instance-attribute

Functions

djstripe.models.billing.InvoiceItem.__str__()
Source code in djstripe/models/billing.py
1239
1240
def __str__(self):
    return self.description
djstripe.models.billing.InvoiceItem.api_retrieve(*args, **kwargs)
Source code in djstripe/models/billing.py
1245
1246
1247
1248
1249
1250
1251
1252
1253
def api_retrieve(self, *args, **kwargs):
    if "-il_" in self.id:
        warnings.warn(
            f"Attempting to retrieve InvoiceItem with id={self.id!r}"
            " will most likely fail. "
            "Run manage.py djstripe_update_invoiceitem_ids if this is a problem."
        )

    return super().api_retrieve(*args, **kwargs)
djstripe.models.billing.InvoiceItem.get_stripe_dashboard_url()
Source code in djstripe/models/billing.py
1242
1243
def get_stripe_dashboard_url(self):
    return self.invoice.get_stripe_dashboard_url()

djstripe.models.billing.InvoiceOrLineItem

Bases: Model

An Internal Model that abstracts InvoiceItem and lineItem objects

Contains two fields: id and type: - id is the id of the Stripe object. - type can be line_item, invoice_item or unsupported

Source code in djstripe/models/billing.py
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
class InvoiceOrLineItem(models.Model):
    """An Internal Model that abstracts InvoiceItem and lineItem
    objects

    Contains two fields: `id` and `type`:
    - `id` is the id of the Stripe object.
    - `type` can be `line_item`, `invoice_item` or `unsupported`
    """

    id = models.CharField(max_length=255, primary_key=True)
    type = StripeEnumField(
        enum=enums.InvoiceorLineItemType,
        help_text=(
            "Indicates whether the underlying model is LineItem or InvoiceItem. Can be"
            " one of: 'invoice_item', 'line_item' or 'unsupported'"
        ),
    )

    @classmethod
    def _model_type(cls, id_):
        if id_.startswith("ii"):
            return InvoiceItem, "invoice_item"
        elif id_.startswith("il"):
            return LineItem, "line_item"
        raise ValueError(f"Unknown object type with id: {id_}")

    @classmethod
    def _get_or_create_from_stripe_object(
        cls,
        data,
        field_name="id",
        refetch=True,
        current_ids=None,
        pending_relations=None,
        save=True,
        stripe_account=None,
        api_key=djstripe_settings.STRIPE_SECRET_KEY,
    ):
        raw_field_data = data.get(field_name)
        id_ = get_id_from_stripe_data(raw_field_data)

        try:
            object_cls, object_type = cls._model_type(id_)
        except ValueError:
            # This may happen if we have object types we don't know about.
            # Let's not make dj-stripe entirely unusable if that happens.
            logger.warning(f"Unknown Object. Could not sync object with id: {id_}")
            return cls.objects.get_or_create(id=id_, defaults={"type": "unsupported"})

        # call model's _get_or_create_from_stripe_object to ensure
        # that object exists before getting or creating its InvoiceorLineItem mapping
        object_cls._get_or_create_from_stripe_object(
            data,
            field_name,
            refetch=refetch,
            current_ids=current_ids,
            pending_relations=pending_relations,
            stripe_account=stripe_account,
            api_key=api_key,
        )

        return cls.objects.get_or_create(id=id_, defaults={"type": object_type})

Attributes

djstripe.models.billing.InvoiceOrLineItem.id = models.CharField(max_length=255, primary_key=True) class-attribute instance-attribute
djstripe.models.billing.InvoiceOrLineItem.type = StripeEnumField(enum=enums.InvoiceorLineItemType, help_text="Indicates whether the underlying model is LineItem or InvoiceItem. Can be one of: 'invoice_item', 'line_item' or 'unsupported'") class-attribute instance-attribute

djstripe.models.billing.LineItem

Bases: StripeModel

The individual line items that make up the invoice.

Stripe documentation: https://stripe.com/docs/api/invoices/line_item

Source code in djstripe/models/billing.py
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
class LineItem(StripeModel):
    """
    The individual line items that make up the invoice.

    Stripe documentation: https://stripe.com/docs/api/invoices/line_item
    """

    stripe_class = stripe.InvoiceLineItem
    expand_fields = ["discounts"]

    amount = StripeQuantumCurrencyAmountField(help_text="The amount, in cents.")
    amount_excluding_tax = StripeQuantumCurrencyAmountField(
        help_text=(
            "The integer amount in cents representing the amount for this line item,"
            " excluding all tax and discounts."
        )
    )
    currency = StripeCurrencyCodeField()
    discount_amounts = JSONField(
        null=True,
        blank=True,
        help_text="The amount of discount calculated per discount for this line item.",
    )
    discountable = models.BooleanField(
        default=False,
        help_text=(
            "If True, discounts will apply to this line item. "
            "Always False for prorations."
        ),
    )
    discounts = JSONField(
        null=True,
        blank=True,
        help_text=(
            "The discounts applied to the invoice line item. Line item discounts are"
            " applied before invoice discounts."
        ),
    )
    invoice_item = StripeForeignKey(
        "InvoiceItem",
        null=True,
        blank=True,
        on_delete=models.CASCADE,
        help_text="The ID of the invoice item associated with this line item if any.",
    )
    period = JSONField(
        help_text=(
            "The period this line_item covers. For subscription line items, this is the"
            " subscription period. For prorations, this starts when the proration was"
            " calculated, and ends at the period end of the subscription. For invoice"
            " items, this is the time at which the invoice item was created or the"
            " period of the item."
        )
    )
    period_end = StripeDateTimeField(
        help_text=(
            "The end of the period, which must be greater than or equal to the start."
        )
    )
    period_start = StripeDateTimeField(help_text="The start of the period.")
    price = JSONField(
        help_text="The price of the line item.",
    )
    proration = models.BooleanField(
        default=False,
        help_text=(
            "Whether or not the invoice item was created automatically as a "
            "proration adjustment when the customer switched plans."
        ),
    )
    proration_details = JSONField(
        help_text="Additional details for proration line items"
    )
    subscription = StripeForeignKey(
        "Subscription",
        null=True,
        blank=True,
        on_delete=models.CASCADE,
        help_text="The subscription that the invoice item pertains to, if any.",
    )
    subscription_item = StripeForeignKey(
        "SubscriptionItem",
        null=True,
        blank=True,
        on_delete=models.CASCADE,
        help_text=(
            "The subscription item that generated this invoice item. Left empty if the"
            " line item is not an explicit result of a subscription."
        ),
    )
    tax_amounts = JSONField(
        null=True,
        blank=True,
        help_text="The amount of tax calculated per tax rate for this line item",
    )
    tax_rates = JSONField(
        null=True, blank=True, help_text="The tax rates which apply to the line item."
    )
    type = StripeEnumField(enum=enums.LineItem)
    unit_amount_excluding_tax = StripeDecimalCurrencyAmountField(
        null=True,
        blank=True,
        help_text=(
            "The amount in cents representing the unit amount for this line item,"
            " excluding all tax and discounts."
        ),
    )
    quantity = models.IntegerField(
        null=True,
        blank=True,
        help_text=(
            "The quantity of the subscription, if the line item is a subscription or a"
            " proration."
        ),
    )

    @classmethod
    def _manipulate_stripe_object_hook(cls, data):
        data["period_start"] = data["period"]["start"]
        data["period_end"] = data["period"]["end"]

        return data

    def _attach_objects_post_save_hook(
        self,
        cls,
        data,
        api_key=djstripe_settings.STRIPE_SECRET_KEY,
        pending_relations=None,
    ):
        super()._attach_objects_post_save_hook(
            cls, data, api_key=api_key, pending_relations=pending_relations
        )

        # sync every discount
        for discount in self.discounts:
            Discount.sync_from_stripe_data(discount, api_key=api_key)

    @classmethod
    def api_list(cls, api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs):
        """
        Call the stripe API's list operation for this model.
        Note that we only iterate and sync the LineItem associated with the
        passed in Invoice.

        Upcoming invoices are virtual and are not saved and hence their
        line items are also not retrieved and synced

        :param api_key: The api key to use for this request. \
            Defaults to djstripe_settings.STRIPE_SECRET_KEY.
        :type api_key: string

        See Stripe documentation for accepted kwargs for each object.

        :returns: an iterator over all items in the query
        """
        # Update kwargs with `expand` param
        kwargs = cls.get_expand_params(api_key, **kwargs)

        # get current invoice if any
        invoice_id = kwargs.pop("id")

        # get expand parameter that needs to be passed to invoice.lines.list call
        expand_fields = kwargs.pop("expand")

        invoice = Invoice.stripe_class.retrieve(invoice_id, api_key=api_key, **kwargs)

        # iterate over all the line items on the current invoice
        return invoice.lines.list(
            api_key=api_key, expand=expand_fields, **kwargs
        ).auto_paging_iter()

Attributes

djstripe.models.billing.LineItem.amount = StripeQuantumCurrencyAmountField(help_text='The amount, in cents.') class-attribute instance-attribute
djstripe.models.billing.LineItem.amount_excluding_tax = StripeQuantumCurrencyAmountField(help_text='The integer amount in cents representing the amount for this line item, excluding all tax and discounts.') class-attribute instance-attribute
djstripe.models.billing.LineItem.currency = StripeCurrencyCodeField() class-attribute instance-attribute
djstripe.models.billing.LineItem.discount_amounts = JSONField(null=True, blank=True, help_text='The amount of discount calculated per discount for this line item.') class-attribute instance-attribute
djstripe.models.billing.LineItem.discountable = models.BooleanField(default=False, help_text='If True, discounts will apply to this line item. Always False for prorations.') class-attribute instance-attribute
djstripe.models.billing.LineItem.discounts = JSONField(null=True, blank=True, help_text='The discounts applied to the invoice line item. Line item discounts are applied before invoice discounts.') class-attribute instance-attribute
djstripe.models.billing.LineItem.expand_fields = ['discounts'] class-attribute instance-attribute
djstripe.models.billing.LineItem.invoice_item = StripeForeignKey('InvoiceItem', null=True, blank=True, on_delete=models.CASCADE, help_text='The ID of the invoice item associated with this line item if any.') class-attribute instance-attribute
djstripe.models.billing.LineItem.period = JSONField(help_text='The period this line_item covers. For subscription line items, this is the subscription period. For prorations, this starts when the proration was calculated, and ends at the period end of the subscription. For invoice items, this is the time at which the invoice item was created or the period of the item.') class-attribute instance-attribute
djstripe.models.billing.LineItem.period_end = StripeDateTimeField(help_text='The end of the period, which must be greater than or equal to the start.') class-attribute instance-attribute
djstripe.models.billing.LineItem.period_start = StripeDateTimeField(help_text='The start of the period.') class-attribute instance-attribute
djstripe.models.billing.LineItem.price = JSONField(help_text='The price of the line item.') class-attribute instance-attribute
djstripe.models.billing.LineItem.proration = models.BooleanField(default=False, help_text='Whether or not the invoice item was created automatically as a proration adjustment when the customer switched plans.') class-attribute instance-attribute
djstripe.models.billing.LineItem.proration_details = JSONField(help_text='Additional details for proration line items') class-attribute instance-attribute
djstripe.models.billing.LineItem.quantity = models.IntegerField(null=True, blank=True, help_text='The quantity of the subscription, if the line item is a subscription or a proration.') class-attribute instance-attribute
djstripe.models.billing.LineItem.stripe_class = stripe.InvoiceLineItem class-attribute instance-attribute
djstripe.models.billing.LineItem.subscription = StripeForeignKey('Subscription', null=True, blank=True, on_delete=models.CASCADE, help_text='The subscription that the invoice item pertains to, if any.') class-attribute instance-attribute
djstripe.models.billing.LineItem.subscription_item = StripeForeignKey('SubscriptionItem', null=True, blank=True, on_delete=models.CASCADE, help_text='The subscription item that generated this invoice item. Left empty if the line item is not an explicit result of a subscription.') class-attribute instance-attribute
djstripe.models.billing.LineItem.tax_amounts = JSONField(null=True, blank=True, help_text='The amount of tax calculated per tax rate for this line item') class-attribute instance-attribute
djstripe.models.billing.LineItem.tax_rates = JSONField(null=True, blank=True, help_text='The tax rates which apply to the line item.') class-attribute instance-attribute
djstripe.models.billing.LineItem.type = StripeEnumField(enum=enums.LineItem) class-attribute instance-attribute
djstripe.models.billing.LineItem.unit_amount_excluding_tax = StripeDecimalCurrencyAmountField(null=True, blank=True, help_text='The amount in cents representing the unit amount for this line item, excluding all tax and discounts.') class-attribute instance-attribute

Functions

djstripe.models.billing.LineItem.api_list(api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs) classmethod

Call the stripe API's list operation for this model. Note that we only iterate and sync the LineItem associated with the passed in Invoice.

Upcoming invoices are virtual and are not saved and hence their line items are also not retrieved and synced

:param api_key: The api key to use for this request. Defaults to djstripe_settings.STRIPE_SECRET_KEY. :type api_key: string

See Stripe documentation for accepted kwargs for each object.

:returns: an iterator over all items in the query

Source code in djstripe/models/billing.py
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
@classmethod
def api_list(cls, api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs):
    """
    Call the stripe API's list operation for this model.
    Note that we only iterate and sync the LineItem associated with the
    passed in Invoice.

    Upcoming invoices are virtual and are not saved and hence their
    line items are also not retrieved and synced

    :param api_key: The api key to use for this request. \
        Defaults to djstripe_settings.STRIPE_SECRET_KEY.
    :type api_key: string

    See Stripe documentation for accepted kwargs for each object.

    :returns: an iterator over all items in the query
    """
    # Update kwargs with `expand` param
    kwargs = cls.get_expand_params(api_key, **kwargs)

    # get current invoice if any
    invoice_id = kwargs.pop("id")

    # get expand parameter that needs to be passed to invoice.lines.list call
    expand_fields = kwargs.pop("expand")

    invoice = Invoice.stripe_class.retrieve(invoice_id, api_key=api_key, **kwargs)

    # iterate over all the line items on the current invoice
    return invoice.lines.list(
        api_key=api_key, expand=expand_fields, **kwargs
    ).auto_paging_iter()

djstripe.models.billing.Plan

Bases: StripeModel

A subscription plan contains the pricing information for different products and feature levels on your site.

Stripe documentation: https://stripe.com/docs/api/plans?lang=python

NOTE: The Stripe Plans API has been deprecated in favor of the Prices API. You may want to upgrade to use the Price model instead of the Plan model.

Source code in djstripe/models/billing.py
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
class Plan(StripeModel):
    """
    A subscription plan contains the pricing information for different
    products and feature levels on your site.

    Stripe documentation: https://stripe.com/docs/api/plans?lang=python

    NOTE: The Stripe Plans API has been deprecated in favor of the Prices API.
    You may want to upgrade to use the Price model instead of the Plan model.
    """

    stripe_class = stripe.Plan
    expand_fields = ["product", "tiers"]
    stripe_dashboard_item_name = "plans"

    active = models.BooleanField(
        help_text="Whether the plan can be used for new purchases."
    )
    aggregate_usage = StripeEnumField(
        enum=enums.PlanAggregateUsage,
        default="",
        blank=True,
        help_text=(
            "Specifies a usage aggregation strategy for plans of usage_type=metered. "
            "Allowed values are `sum` for summing up all usage during a period, "
            "`last_during_period` for picking the last usage record reported within a "
            "period, `last_ever` for picking the last usage record ever (across period "
            "bounds) or max which picks the usage record with the maximum reported "
            "usage during a period. Defaults to `sum`."
        ),
    )
    amount = StripeDecimalCurrencyAmountField(
        null=True,
        blank=True,
        help_text="Amount (as decimal) to be charged on the interval specified.",
    )
    amount_decimal = StripeDecimalCurrencyAmountField(
        null=True,
        blank=True,
        max_digits=19,
        decimal_places=12,
        help_text=(
            "The unit amount in cents to be charged, represented as a decimal "
            "string with at most 12 decimal places."
        ),
    )
    billing_scheme = StripeEnumField(
        enum=enums.BillingScheme,
        default="",
        blank=True,
        help_text=(
            "Describes how to compute the price per period. "
            "Either `per_unit` or `tiered`. "
            "`per_unit` indicates that the fixed amount (specified in amount) "
            "will be charged per unit in quantity "
            "(for plans with `usage_type=licensed`), or per unit of total "
            "usage (for plans with `usage_type=metered`). "
            "`tiered` indicates that the unit pricing will be computed using "
            "a tiering strategy as defined using the tiers and tiers_mode attributes."
        ),
    )
    currency = StripeCurrencyCodeField()
    interval = StripeEnumField(
        enum=enums.PlanInterval,
        help_text="The frequency with which a subscription should be billed.",
    )
    interval_count = models.PositiveIntegerField(
        null=True,
        blank=True,
        help_text=(
            "The number of intervals (specified in the interval property) "
            "between each subscription billing."
        ),
    )
    nickname = models.TextField(
        max_length=5000,
        default="",
        blank=True,
        help_text="A brief description of the plan, hidden from customers.",
    )
    product = StripeForeignKey(
        "Product",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        help_text="The product whose pricing this plan determines.",
    )
    tiers = JSONField(
        null=True,
        blank=True,
        help_text=(
            "Each element represents a pricing tier. "
            "This parameter requires `billing_scheme` to be set to `tiered`."
        ),
    )
    tiers_mode = StripeEnumField(
        enum=enums.PriceTiersMode,
        null=True,
        blank=True,
        help_text=(
            "Defines if the tiering price should be `graduated` or `volume` based. "
            "In `volume`-based tiering, the maximum quantity within a period "
            "determines the per unit price, in `graduated` tiering pricing can "
            "successively change as the quantity grows."
        ),
    )
    transform_usage = JSONField(
        null=True,
        blank=True,
        help_text=(
            "Apply a transformation to the reported usage or set quantity "
            "before computing the billed price. Cannot be combined with `tiers`."
        ),
    )
    trial_period_days = models.IntegerField(
        null=True,
        blank=True,
        help_text=(
            "Number of trial period days granted when subscribing a customer "
            "to this plan. Null if the plan has no trial period."
        ),
    )
    usage_type = StripeEnumField(
        enum=enums.PriceUsageType,
        default=enums.PriceUsageType.licensed,
        help_text=(
            "Configures how the quantity per period should be determined, "
            "can be either `metered` or `licensed`. `licensed` will automatically "
            "bill the `quantity` set for a plan when adding it to a subscription, "
            "`metered` will aggregate the total usage based on usage records. "
            "Defaults to `licensed`."
        ),
    )

    class Meta(object):
        ordering = ["amount"]

    @classmethod
    def get_or_create(cls, **kwargs):
        """Get or create a Plan."""

        try:
            return cls.objects.get(id=kwargs["id"]), False
        except cls.DoesNotExist:
            return cls.create(**kwargs), True

    @classmethod
    def create(cls, **kwargs):
        # A few minor things are changed in the api-version of the create call
        api_kwargs = dict(kwargs)
        api_kwargs["amount"] = int(api_kwargs["amount"] * 100)

        if isinstance(api_kwargs.get("product"), StripeModel):
            api_kwargs["product"] = api_kwargs["product"].id

        stripe_plan = cls._api_create(**api_kwargs)
        api_key = api_kwargs.get("api_key") or djstripe_settings.STRIPE_SECRET_KEY
        plan = cls.sync_from_stripe_data(stripe_plan, api_key=api_key)

        return plan

    def __str__(self):
        if self.product and self.product.name:
            return f"{self.human_readable_price} for {self.product.name}"
        return self.human_readable_price

    @property
    def amount_in_cents(self):
        return int(self.amount * 100)

    @property
    def human_readable_price(self) -> str:
        if self.billing_scheme == "per_unit":
            unit_amount = self.amount
            amount = get_friendly_currency_amount(unit_amount, self.currency)
        else:
            # tiered billing scheme
            tier_1 = self.tiers[0]
            flat_amount_tier_1 = tier_1["flat_amount"]
            formatted_unit_amount_tier_1 = get_friendly_currency_amount(
                (tier_1["unit_amount"] or 0) / 100, self.currency
            )
            amount = f"Starts at {formatted_unit_amount_tier_1} per unit"

            # stripe shows flat fee even if it is set to 0.00
            if flat_amount_tier_1 is not None:
                formatted_flat_amount_tier_1 = get_friendly_currency_amount(
                    flat_amount_tier_1 / 100, self.currency
                )
                amount = f"{amount} + {formatted_flat_amount_tier_1}"

        format_args = {"amount": amount}

        interval_count = self.interval_count
        if interval_count == 1:
            interval = {
                "day": _("day"),
                "week": _("week"),
                "month": _("month"),
                "year": _("year"),
            }[self.interval]
            template = _("{amount}/{interval}")
            format_args["interval"] = interval
        else:
            interval = {
                "day": _("days"),
                "week": _("weeks"),
                "month": _("months"),
                "year": _("years"),
            }[self.interval]
            template = _("{amount} / every {interval_count} {interval}")
            format_args["interval"] = interval
            format_args["interval_count"] = interval_count

        return str(format_lazy(template, **format_args))

Attributes

djstripe.models.billing.Plan.active = models.BooleanField(help_text='Whether the plan can be used for new purchases.') class-attribute instance-attribute
djstripe.models.billing.Plan.aggregate_usage = StripeEnumField(enum=enums.PlanAggregateUsage, default='', blank=True, help_text='Specifies a usage aggregation strategy for plans of usage_type=metered. Allowed values are `sum` for summing up all usage during a period, `last_during_period` for picking the last usage record reported within a period, `last_ever` for picking the last usage record ever (across period bounds) or max which picks the usage record with the maximum reported usage during a period. Defaults to `sum`.') class-attribute instance-attribute
djstripe.models.billing.Plan.amount = StripeDecimalCurrencyAmountField(null=True, blank=True, help_text='Amount (as decimal) to be charged on the interval specified.') class-attribute instance-attribute
djstripe.models.billing.Plan.amount_decimal = StripeDecimalCurrencyAmountField(null=True, blank=True, max_digits=19, decimal_places=12, help_text='The unit amount in cents to be charged, represented as a decimal string with at most 12 decimal places.') class-attribute instance-attribute
djstripe.models.billing.Plan.amount_in_cents property
djstripe.models.billing.Plan.billing_scheme = StripeEnumField(enum=enums.BillingScheme, default='', blank=True, help_text='Describes how to compute the price per period. Either `per_unit` or `tiered`. `per_unit` indicates that the fixed amount (specified in amount) will be charged per unit in quantity (for plans with `usage_type=licensed`), or per unit of total usage (for plans with `usage_type=metered`). `tiered` indicates that the unit pricing will be computed using a tiering strategy as defined using the tiers and tiers_mode attributes.') class-attribute instance-attribute
djstripe.models.billing.Plan.currency = StripeCurrencyCodeField() class-attribute instance-attribute
djstripe.models.billing.Plan.expand_fields = ['product', 'tiers'] class-attribute instance-attribute
djstripe.models.billing.Plan.human_readable_price: str property
djstripe.models.billing.Plan.interval = StripeEnumField(enum=enums.PlanInterval, help_text='The frequency with which a subscription should be billed.') class-attribute instance-attribute
djstripe.models.billing.Plan.interval_count = models.PositiveIntegerField(null=True, blank=True, help_text='The number of intervals (specified in the interval property) between each subscription billing.') class-attribute instance-attribute
djstripe.models.billing.Plan.nickname = models.TextField(max_length=5000, default='', blank=True, help_text='A brief description of the plan, hidden from customers.') class-attribute instance-attribute
djstripe.models.billing.Plan.product = StripeForeignKey('Product', on_delete=models.SET_NULL, null=True, blank=True, help_text='The product whose pricing this plan determines.') class-attribute instance-attribute
djstripe.models.billing.Plan.stripe_class = stripe.Plan class-attribute instance-attribute
djstripe.models.billing.Plan.stripe_dashboard_item_name = 'plans' class-attribute instance-attribute
djstripe.models.billing.Plan.tiers = JSONField(null=True, blank=True, help_text='Each element represents a pricing tier. This parameter requires `billing_scheme` to be set to `tiered`.') class-attribute instance-attribute
djstripe.models.billing.Plan.tiers_mode = StripeEnumField(enum=enums.PriceTiersMode, null=True, blank=True, help_text='Defines if the tiering price should be `graduated` or `volume` based. In `volume`-based tiering, the maximum quantity within a period determines the per unit price, in `graduated` tiering pricing can successively change as the quantity grows.') class-attribute instance-attribute
djstripe.models.billing.Plan.transform_usage = JSONField(null=True, blank=True, help_text='Apply a transformation to the reported usage or set quantity before computing the billed price. Cannot be combined with `tiers`.') class-attribute instance-attribute
djstripe.models.billing.Plan.trial_period_days = models.IntegerField(null=True, blank=True, help_text='Number of trial period days granted when subscribing a customer to this plan. Null if the plan has no trial period.') class-attribute instance-attribute
djstripe.models.billing.Plan.usage_type = StripeEnumField(enum=enums.PriceUsageType, default=enums.PriceUsageType.licensed, help_text='Configures how the quantity per period should be determined, can be either `metered` or `licensed`. `licensed` will automatically bill the `quantity` set for a plan when adding it to a subscription, `metered` will aggregate the total usage based on usage records. Defaults to `licensed`.') class-attribute instance-attribute

Classes

djstripe.models.billing.Plan.Meta

Bases: object

Source code in djstripe/models/billing.py
1627
1628
class Meta(object):
    ordering = ["amount"]
Attributes
djstripe.models.billing.Plan.Meta.ordering = ['amount'] class-attribute instance-attribute

Functions

djstripe.models.billing.Plan.__str__()
Source code in djstripe/models/billing.py
1654
1655
1656
1657
def __str__(self):
    if self.product and self.product.name:
        return f"{self.human_readable_price} for {self.product.name}"
    return self.human_readable_price
djstripe.models.billing.Plan.create(**kwargs) classmethod
Source code in djstripe/models/billing.py
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
@classmethod
def create(cls, **kwargs):
    # A few minor things are changed in the api-version of the create call
    api_kwargs = dict(kwargs)
    api_kwargs["amount"] = int(api_kwargs["amount"] * 100)

    if isinstance(api_kwargs.get("product"), StripeModel):
        api_kwargs["product"] = api_kwargs["product"].id

    stripe_plan = cls._api_create(**api_kwargs)
    api_key = api_kwargs.get("api_key") or djstripe_settings.STRIPE_SECRET_KEY
    plan = cls.sync_from_stripe_data(stripe_plan, api_key=api_key)

    return plan
djstripe.models.billing.Plan.get_or_create(**kwargs) classmethod

Get or create a Plan.

Source code in djstripe/models/billing.py
1630
1631
1632
1633
1634
1635
1636
1637
@classmethod
def get_or_create(cls, **kwargs):
    """Get or create a Plan."""

    try:
        return cls.objects.get(id=kwargs["id"]), False
    except cls.DoesNotExist:
        return cls.create(**kwargs), True

djstripe.models.billing.ShippingRate

Bases: StripeModel

Shipping rates describe the price of shipping presented to your customers and can be applied to Checkout Sessions to collect shipping costs.

Stripe documentation: https://stripe.com/docs/api/shipping_rates

Source code in djstripe/models/billing.py
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
class ShippingRate(StripeModel):
    """
    Shipping rates describe the price of shipping presented
    to your customers and can be applied to Checkout Sessions
    to collect shipping costs.

    Stripe documentation: https://stripe.com/docs/api/shipping_rates
    """

    stripe_class = stripe.ShippingRate
    stripe_dashboard_item_name = "shipping-rates"
    description = None

    active = models.BooleanField(
        default=True,
        help_text=(
            "Whether the shipping rate can be used for new purchases. Defaults to true"
        ),
    )
    display_name = models.CharField(
        max_length=50,
        default="",
        blank=True,
        help_text=(
            "The name of the shipping rate, meant to be displayable to the customer."
            " This will appear on CheckoutSessions."
        ),
    )
    fixed_amount = JSONField(
        help_text=(
            "Describes a fixed amount to charge for shipping. Must be present if type"
            " is fixed_amount"
        ),
    )
    type = StripeEnumField(
        enum=enums.ShippingRateType,
        default=enums.ShippingRateType.fixed_amount,
        help_text=_(
            "The type of calculation to use on the shipping rate. Can only be"
            " fixed_amount for now."
        ),
    )
    delivery_estimate = JSONField(
        null=True,
        blank=True,
        help_text=(
            "The estimated range for how long shipping will take, meant to be"
            " displayable to the customer. This will appear on CheckoutSessions."
        ),
    )
    tax_behavior = StripeEnumField(
        enum=enums.ShippingRateTaxBehavior,
        help_text=_(
            "Specifies whether the rate is considered inclusive of taxes or exclusive"
            " of taxes."
        ),
    )
    tax_code = StripeForeignKey(
        "TaxCode",
        null=True,
        blank=True,
        on_delete=models.CASCADE,
        help_text="The shipping tax code",
    )

    class Meta(StripeModel.Meta):
        verbose_name = "Shipping Rate"

    def __str__(self):
        amount = get_friendly_currency_amount(
            self.fixed_amount.get("amount") / 100, self.fixed_amount.get("currency")
        )
        if self.active:
            return f"{self.display_name} - {amount} (Active)"
        else:
            return f"{self.display_name} - {amount} (Archived)"

Attributes

djstripe.models.billing.ShippingRate.active = models.BooleanField(default=True, help_text='Whether the shipping rate can be used for new purchases. Defaults to true') class-attribute instance-attribute
djstripe.models.billing.ShippingRate.delivery_estimate = JSONField(null=True, blank=True, help_text='The estimated range for how long shipping will take, meant to be displayable to the customer. This will appear on CheckoutSessions.') class-attribute instance-attribute
djstripe.models.billing.ShippingRate.description = None class-attribute instance-attribute
djstripe.models.billing.ShippingRate.display_name = models.CharField(max_length=50, default='', blank=True, help_text='The name of the shipping rate, meant to be displayable to the customer. This will appear on CheckoutSessions.') class-attribute instance-attribute
djstripe.models.billing.ShippingRate.fixed_amount = JSONField(help_text='Describes a fixed amount to charge for shipping. Must be present if type is fixed_amount') class-attribute instance-attribute
djstripe.models.billing.ShippingRate.stripe_class = stripe.ShippingRate class-attribute instance-attribute
djstripe.models.billing.ShippingRate.stripe_dashboard_item_name = 'shipping-rates' class-attribute instance-attribute
djstripe.models.billing.ShippingRate.tax_behavior = StripeEnumField(enum=enums.ShippingRateTaxBehavior, help_text=_('Specifies whether the rate is considered inclusive of taxes or exclusive of taxes.')) class-attribute instance-attribute
djstripe.models.billing.ShippingRate.tax_code = StripeForeignKey('TaxCode', null=True, blank=True, on_delete=models.CASCADE, help_text='The shipping tax code') class-attribute instance-attribute
djstripe.models.billing.ShippingRate.type = StripeEnumField(enum=enums.ShippingRateType, default=enums.ShippingRateType.fixed_amount, help_text=_('The type of calculation to use on the shipping rate. Can only be fixed_amount for now.')) class-attribute instance-attribute

Classes

djstripe.models.billing.ShippingRate.Meta

Bases: Meta

Source code in djstripe/models/billing.py
2549
2550
class Meta(StripeModel.Meta):
    verbose_name = "Shipping Rate"
Attributes
djstripe.models.billing.ShippingRate.Meta.verbose_name = 'Shipping Rate' class-attribute instance-attribute

Functions

djstripe.models.billing.ShippingRate.__str__()
Source code in djstripe/models/billing.py
2552
2553
2554
2555
2556
2557
2558
2559
def __str__(self):
    amount = get_friendly_currency_amount(
        self.fixed_amount.get("amount") / 100, self.fixed_amount.get("currency")
    )
    if self.active:
        return f"{self.display_name} - {amount} (Active)"
    else:
        return f"{self.display_name} - {amount} (Archived)"

djstripe.models.billing.Subscription

Bases: StripeModel

Subscriptions allow you to charge a customer's card on a recurring basis. A subscription ties a customer to a particular plan you've created.

A subscription still in its trial period is trialing and moves to active when the trial period is over.

When payment to renew the subscription fails, the subscription becomes past_due. After Stripe has exhausted all payment retry attempts, the subscription ends up with a status of either canceled or unpaid depending on your retry settings.

Note that when a subscription has a status of unpaid, no subsequent invoices will be attempted (invoices will be created, but then immediately automatically closed.

Additionally, updating customer card details will not lead to Stripe retrying the latest invoice.). After receiving updated card details from a customer, you may choose to reopen and pay their closed invoices.

Stripe documentation: https://stripe.com/docs/api?lang=python#subscriptions

Source code in djstripe/models/billing.py
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
class Subscription(StripeModel):
    """
    Subscriptions allow you to charge a customer's card on a recurring basis.
    A subscription ties a customer to a particular plan you've created.

    A subscription still in its trial period is ``trialing`` and moves to ``active``
    when the trial period is over.

    When payment to renew the subscription fails, the subscription becomes ``past_due``.
    After Stripe has exhausted all payment retry attempts, the subscription ends up
    with a status of either ``canceled`` or ``unpaid`` depending on your retry settings.

    Note that when a subscription has a status of ``unpaid``, no subsequent invoices
    will be attempted (invoices will be created, but then immediately
    automatically closed.

    Additionally, updating customer card details will not lead to Stripe retrying the
    latest invoice.).
    After receiving updated card details from a customer, you may choose to reopen and
    pay their closed invoices.

    Stripe documentation: https://stripe.com/docs/api?lang=python#subscriptions
    """

    stripe_class = stripe.Subscription
    stripe_dashboard_item_name = "subscriptions"

    application_fee_percent = StripePercentField(
        null=True,
        blank=True,
        help_text=(
            "A positive decimal that represents the fee percentage of the "
            "subscription invoice amount that will be transferred to the application "
            "owner's Stripe account each billing period."
        ),
    )
    billing_cycle_anchor = StripeDateTimeField(
        null=True,
        blank=True,
        help_text=(
            "Determines the date of the first full invoice, and, for plans "
            "with `month` or `year` intervals, the day of the month for subsequent "
            "invoices."
        ),
    )
    billing_thresholds = JSONField(
        null=True,
        blank=True,
        help_text=(
            "Define thresholds at which an invoice will be sent, and the "
            "subscription advanced to a new billing period."
        ),
    )
    cancel_at = StripeDateTimeField(
        null=True,
        blank=True,
        help_text=(
            "A date in the future at which the subscription will automatically "
            "get canceled."
        ),
    )
    cancel_at_period_end = models.BooleanField(
        default=False,
        help_text=(
            "If the subscription has been canceled with the ``at_period_end`` flag set"
            " to true, ``cancel_at_period_end`` on the subscription will be true. You"
            " can use this attribute to determine whether a subscription that has a"
            " status of active is scheduled to be canceled at the end of the current"
            " period."
        ),
    )
    canceled_at = StripeDateTimeField(
        null=True,
        blank=True,
        help_text=(
            "If the subscription has been canceled, the date of that cancellation. If"
            " the subscription was canceled with ``cancel_at_period_end``, canceled_at"
            " will still reflect the date of the initial cancellation request, not the"
            " end of the subscription period when the subscription is automatically"
            " moved to a canceled state."
        ),
    )
    collection_method = StripeEnumField(
        enum=enums.InvoiceCollectionMethod,
        help_text=(
            "Either `charge_automatically`, or `send_invoice`. When charging"
            " automatically, Stripe will attempt to pay this subscription at the end of"
            " the cycle using the default source attached to the customer. When sending"
            " an invoice, Stripe will email your customer an invoice with payment"
            " instructions."
        ),
    )
    current_period_end = StripeDateTimeField(
        help_text=(
            "End of the current period for which the subscription has been "
            "invoiced. At the end of this period, a new invoice will be created."
        )
    )
    current_period_start = StripeDateTimeField(
        help_text=(
            "Start of the current period for which the subscription has been invoiced."
        )
    )
    customer = StripeForeignKey(
        "Customer",
        on_delete=models.CASCADE,
        related_name="subscriptions",
        help_text="The customer associated with this subscription.",
    )
    days_until_due = models.IntegerField(
        null=True,
        blank=True,
        help_text=(
            "Number of days a customer has to pay invoices generated by this "
            "subscription. This value will be `null` for subscriptions where "
            "`billing=charge_automatically`."
        ),
    )
    default_payment_method = StripeForeignKey(
        "PaymentMethod",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+",
        help_text=(
            "The default payment method for the subscription. "
            "It must belong to the customer associated with the subscription. "
            "If not set, invoices will use the default payment method in the "
            "customer's invoice settings."
        ),
    )
    default_source = PaymentMethodForeignKey(
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="subscriptions",
        help_text=(
            "The default payment source for the subscription. "
            "It must belong to the customer associated with the subscription "
            "and be in a chargeable state. If not set, defaults to the customer's "
            "default source."
        ),
    )
    default_tax_rates = models.ManyToManyField(
        "TaxRate",
        # explicitly specify the joining table name as though the joining model
        # was defined with through="DjstripeSubscriptionDefaultTaxRate"
        db_table="djstripe_djstripesubscriptiondefaulttaxrate",
        related_name="+",
        blank=True,
        help_text=(
            "The tax rates that will apply to any subscription item "
            "that does not have tax_rates set. Invoices created will have their "
            "default_tax_rates populated from the subscription."
        ),
    )
    discount = JSONField(
        null=True,
        blank=True,
        help_text=(
            "Describes the current discount applied to this subscription, if there is"
            " one. When billing, a discount applied to a subscription overrides a"
            " discount applied on a customer-wide basis."
        ),
    )
    ended_at = StripeDateTimeField(
        null=True,
        blank=True,
        help_text=(
            "If the subscription has ended (either because it was canceled or "
            "because the customer was switched to a subscription to a new plan), "
            "the date the subscription ended."
        ),
    )
    latest_invoice = StripeForeignKey(
        "Invoice",
        null=True,
        blank=True,
        related_name="+",
        on_delete=models.SET_NULL,
        help_text="The most recent invoice this subscription has generated.",
    )
    next_pending_invoice_item_invoice = StripeDateTimeField(
        null=True,
        blank=True,
        help_text=(
            "Specifies the approximate timestamp on which any pending "
            "invoice items will be billed according to the schedule provided at "
            "pending_invoice_item_interval."
        ),
    )
    pause_collection = JSONField(
        null=True,
        blank=True,
        help_text=(
            "If specified, payment collection for this subscription will be paused."
        ),
    )
    pending_invoice_item_interval = JSONField(
        null=True,
        blank=True,
        help_text=(
            "Specifies an interval for how often to bill for any "
            "pending invoice items. It is analogous to calling Create an invoice "
            "for the given subscription at the specified interval."
        ),
    )
    pending_setup_intent = StripeForeignKey(
        "SetupIntent",
        null=True,
        blank=True,
        on_delete=models.CASCADE,
        related_name="setup_intents",
        help_text=(
            "We can use this SetupIntent to collect user authentication "
            "when creating a subscription without immediate payment or updating a "
            "subscription's payment method, allowing you to "
            "optimize for off-session payments."
        ),
    )
    pending_update = JSONField(
        null=True,
        blank=True,
        help_text=(
            "If specified, pending updates that will be applied to the "
            "subscription once the latest_invoice has been paid."
        ),
    )
    plan = models.ForeignKey(
        "Plan",
        null=True,
        blank=True,
        on_delete=models.CASCADE,
        related_name="subscriptions",
        help_text=(
            "The plan associated with this subscription. This value will be "
            "`null` for multi-plan subscriptions"
        ),
    )
    proration_behavior = StripeEnumField(
        enum=enums.SubscriptionProrationBehavior,
        help_text=(
            "Determines how to handle prorations when the billing cycle changes (e.g.,"
            " when switching plans, resetting billing_cycle_anchor=now, or starting a"
            " trial), or if an item’s quantity changes"
        ),
        default=enums.SubscriptionProrationBehavior.create_prorations,
        blank=True,
    )
    proration_date = StripeDateTimeField(
        null=True,
        blank=True,
        help_text=(
            "If set, the proration will be calculated as though the subscription was"
            " updated at the given time. This can be used to apply exactly the same"
            " proration that was previewed with upcoming invoice endpoint. It can also"
            " be used to implement custom proration logic, such as prorating by day"
            " instead of by second, by providing the time that you wish to use for"
            " proration calculations"
        ),
    )
    quantity = models.IntegerField(
        null=True,
        blank=True,
        help_text=(
            "The quantity applied to this subscription. This value will be "
            "`null` for multi-plan subscriptions"
        ),
    )
    schedule = models.ForeignKey(
        "SubscriptionSchedule",
        null=True,
        blank=True,
        on_delete=models.CASCADE,
        related_name="subscriptions",
        help_text="The schedule associated with this subscription.",
    )
    start_date = StripeDateTimeField(
        null=True,
        blank=True,
        help_text=(
            "Date when the subscription was first created. The date "
            "might differ from the created date due to backdating."
        ),
    )
    status = StripeEnumField(
        enum=enums.SubscriptionStatus, help_text="The status of this subscription."
    )
    trial_end = StripeDateTimeField(
        null=True,
        blank=True,
        help_text="If the subscription has a trial, the end of that trial.",
    )
    trial_start = StripeDateTimeField(
        null=True,
        blank=True,
        help_text="If the subscription has a trial, the beginning of that trial.",
    )

    objects = SubscriptionManager()

    @classmethod
    def api_list(cls, api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs):
        """
        Call the stripe API's list operation for this model.
        :param api_key: The api key to use for this request. \
            Defaults to djstripe_settings.STRIPE_SECRET_KEY.
        :type api_key: string
        See Stripe documentation for accepted kwargs for each object.
        :returns: an iterator over all items in the query
        """
        # Update kwargs with `expand` param
        kwargs = cls.get_expand_params(api_key, **kwargs)

        if not kwargs.get("status"):
            # special case: https://stripe.com/docs/api/subscriptions/list#list_subscriptions-status
            # See Issue: https://github.com/dj-stripe/dj-stripe/issues/1763
            kwargs["status"] = "all"
        return super().api_list(api_key=api_key, **kwargs)

    def update(self, plan: Union[StripeModel, str] = None, **kwargs):
        """
        See `Customer.subscribe() <#djstripe.models.Customer.subscribe>`__

        :param plan: The plan to which to subscribe the customer.
        :type plan: Plan or string (plan ID)

        .. important:: Updating a subscription by changing the plan or quantity \
            creates a new ``Subscription`` in \
            Stripe (and dj-stripe).
        """

        # Convert Plan to id
        if plan is not None and isinstance(plan, StripeModel):
            plan = plan.id

        stripe_subscription = self._api_update(plan=plan, **kwargs)

        api_key = kwargs.get("api_key") or self.default_api_key
        return Subscription.sync_from_stripe_data(stripe_subscription, api_key=api_key)

    def extend(self, delta):
        """
        Extends this subscription by the provided delta.

        :param delta: The timedelta by which to extend this subscription.
        :type delta: timedelta
        """

        if delta.total_seconds() < 0:
            raise ValueError("delta must be a positive timedelta.")

        if self.trial_end is not None and self.trial_end > timezone.now():
            period_end = self.trial_end
        else:
            period_end = self.current_period_end

        period_end += delta

        return self.update(proration_behavior="none", trial_end=period_end)

    def cancel(self, at_period_end: bool = False, **kwargs):
        """
        Cancels this subscription. If you set the at_period_end parameter to true,
        the subscription will remain active until the end of the period, at which point
        it will be canceled and not renewed. By default, the subscription is terminated
        immediately. In either case, the customer will not be charged again for
        the subscription. Note, however, that any pending invoice items or metered
        usage will still be charged at the end of the period unless manually
        deleted.

        Depending on how `proration_behavior` is set, any pending prorations will
        also be left in place and collected at the end of the period.
        However, if the subscription is set to cancel immediately, you can pass the
        `prorate` and `invoice_now` flags in `kwargs` to configure how the pending
        metered usage is invoiced and how proration must work.

        By default, all unpaid invoices for the customer will be closed upon
        subscription cancellation. We do this in order to prevent unexpected payment
        retries once the customer has canceled a subscription. However, you can
        reopen the invoices manually after subscription cancellation to have us proceed
        with automatic retries, or you could even re-attempt payment yourself on all
        unpaid invoices before allowing the customer to cancel the
        subscription at all.

        :param at_period_end: A flag that if set to true will delay the cancellation \
            of the subscription until the end of the current period. Default is False.
        :type at_period_end: boolean

        .. important:: If a subscription is canceled during a trial period, \
        the ``at_period_end`` flag will be overridden to False so that the trial ends \
        immediately and the customer's card isn't charged.
        """

        # If plan has trial days and customer cancels before
        # trial period ends, then end subscription now,
        # i.e. at_period_end=False
        if self.trial_end and self.trial_end > timezone.now():
            at_period_end = False

        if at_period_end:
            stripe_subscription = self._api_update(cancel_at_period_end=True, **kwargs)
        else:
            try:
                stripe_subscription = self._api_delete(**kwargs)
            except InvalidRequestError as exc:
                if "No such subscription:" in str(exc):
                    # cancel() works by deleting the subscription. The object still
                    # exists in Stripe however, and can still be retrieved.
                    # If the subscription was already canceled (status=canceled),
                    # that api_retrieve() call will fail with "No such subscription".
                    # However, this may also happen if the subscription legitimately
                    # does not exist, in which case the following line will re-raise.
                    stripe_subscription = self.api_retrieve()
                else:
                    raise

        return Subscription.sync_from_stripe_data(
            stripe_subscription, api_key=self.default_api_key
        )

    def reactivate(self):
        """
        Reactivates this subscription.

        If a customer's subscription is canceled with ``at_period_end`` set to True and
        it has not yet reached the end of the billing period, it can be reactivated.
        Subscriptions canceled immediately cannot be reactivated.
        (Source: https://stripe.com/docs/billing/subscriptions/cancel)

        .. warning:: Reactivating a fully canceled Subscription will fail silently. \
        Be sure to check the returned Subscription's status.
        """
        stripe_subscription = self.api_retrieve()
        stripe_subscription.plan = self.plan.id
        stripe_subscription.cancel_at_period_end = False

        return Subscription.sync_from_stripe_data(stripe_subscription.save())

    def is_period_current(self):
        """
        Returns True if this subscription's period is current, false otherwise.
        """

        return self.current_period_end > timezone.now() or (
            self.trial_end and self.trial_end > timezone.now()
        )

    def is_status_current(self):
        """
        Returns True if this subscription's status is current (active or trialing),
        false otherwise.
        """

        return self.status in ["trialing", "active"]

    def is_status_temporarily_current(self):
        """
        A status is temporarily current when the subscription is canceled with the
        ``at_period_end`` flag.
        The subscription is still active, but is technically canceled and we're just
        waiting for it to run out.

        You could use this method to give customers limited service after they've
        canceled. For example, a video on demand service could only allow customers
        to download their libraries and do nothing else when their
        subscription is temporarily current.
        """

        return (
            self.canceled_at
            and self.cancel_at_period_end
            and timezone.now() < self.current_period_end
        )

    def is_valid(self):
        """
        Returns True if this subscription's status and period are current,
        false otherwise.
        """

        if not self.is_status_current():
            return False

        if not self.is_period_current():
            return False

        return True

    def _attach_objects_post_save_hook(
        self,
        cls,
        data,
        api_key=djstripe_settings.STRIPE_SECRET_KEY,
        pending_relations=None,
    ):
        super()._attach_objects_post_save_hook(
            cls, data, api_key=api_key, pending_relations=pending_relations
        )

        cls._stripe_object_to_subscription_items(
            target_cls=SubscriptionItem, data=data, subscription=self, api_key=api_key
        )

        self.default_tax_rates.set(
            cls._stripe_object_to_default_tax_rates(
                target_cls=TaxRate, data=data, api_key=api_key
            )
        )

Attributes

djstripe.models.billing.Subscription.application_fee_percent = StripePercentField(null=True, blank=True, help_text="A positive decimal that represents the fee percentage of the subscription invoice amount that will be transferred to the application owner's Stripe account each billing period.") class-attribute instance-attribute
djstripe.models.billing.Subscription.billing_cycle_anchor = StripeDateTimeField(null=True, blank=True, help_text='Determines the date of the first full invoice, and, for plans with `month` or `year` intervals, the day of the month for subsequent invoices.') class-attribute instance-attribute
djstripe.models.billing.Subscription.billing_thresholds = JSONField(null=True, blank=True, help_text='Define thresholds at which an invoice will be sent, and the subscription advanced to a new billing period.') class-attribute instance-attribute
djstripe.models.billing.Subscription.cancel_at = StripeDateTimeField(null=True, blank=True, help_text='A date in the future at which the subscription will automatically get canceled.') class-attribute instance-attribute
djstripe.models.billing.Subscription.cancel_at_period_end = models.BooleanField(default=False, help_text='If the subscription has been canceled with the ``at_period_end`` flag set to true, ``cancel_at_period_end`` on the subscription will be true. You can use this attribute to determine whether a subscription that has a status of active is scheduled to be canceled at the end of the current period.') class-attribute instance-attribute
djstripe.models.billing.Subscription.canceled_at = StripeDateTimeField(null=True, blank=True, help_text='If the subscription has been canceled, the date of that cancellation. If the subscription was canceled with ``cancel_at_period_end``, canceled_at will still reflect the date of the initial cancellation request, not the end of the subscription period when the subscription is automatically moved to a canceled state.') class-attribute instance-attribute
djstripe.models.billing.Subscription.collection_method = StripeEnumField(enum=enums.InvoiceCollectionMethod, help_text='Either `charge_automatically`, or `send_invoice`. When charging automatically, Stripe will attempt to pay this subscription at the end of the cycle using the default source attached to the customer. When sending an invoice, Stripe will email your customer an invoice with payment instructions.') class-attribute instance-attribute
djstripe.models.billing.Subscription.current_period_end = StripeDateTimeField(help_text='End of the current period for which the subscription has been invoiced. At the end of this period, a new invoice will be created.') class-attribute instance-attribute
djstripe.models.billing.Subscription.current_period_start = StripeDateTimeField(help_text='Start of the current period for which the subscription has been invoiced.') class-attribute instance-attribute
djstripe.models.billing.Subscription.customer = StripeForeignKey('Customer', on_delete=models.CASCADE, related_name='subscriptions', help_text='The customer associated with this subscription.') class-attribute instance-attribute
djstripe.models.billing.Subscription.days_until_due = models.IntegerField(null=True, blank=True, help_text='Number of days a customer has to pay invoices generated by this subscription. This value will be `null` for subscriptions where `billing=charge_automatically`.') class-attribute instance-attribute
djstripe.models.billing.Subscription.default_payment_method = StripeForeignKey('PaymentMethod', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', help_text="The default payment method for the subscription. It must belong to the customer associated with the subscription. If not set, invoices will use the default payment method in the customer's invoice settings.") class-attribute instance-attribute
djstripe.models.billing.Subscription.default_source = PaymentMethodForeignKey(on_delete=models.SET_NULL, null=True, blank=True, related_name='subscriptions', help_text="The default payment source for the subscription. It must belong to the customer associated with the subscription and be in a chargeable state. If not set, defaults to the customer's default source.") class-attribute instance-attribute
djstripe.models.billing.Subscription.default_tax_rates = models.ManyToManyField('TaxRate', db_table='djstripe_djstripesubscriptiondefaulttaxrate', related_name='+', blank=True, help_text='The tax rates that will apply to any subscription item that does not have tax_rates set. Invoices created will have their default_tax_rates populated from the subscription.') class-attribute instance-attribute
djstripe.models.billing.Subscription.discount = JSONField(null=True, blank=True, help_text='Describes the current discount applied to this subscription, if there is one. When billing, a discount applied to a subscription overrides a discount applied on a customer-wide basis.') class-attribute instance-attribute
djstripe.models.billing.Subscription.ended_at = StripeDateTimeField(null=True, blank=True, help_text='If the subscription has ended (either because it was canceled or because the customer was switched to a subscription to a new plan), the date the subscription ended.') class-attribute instance-attribute
djstripe.models.billing.Subscription.latest_invoice = StripeForeignKey('Invoice', null=True, blank=True, related_name='+', on_delete=models.SET_NULL, help_text='The most recent invoice this subscription has generated.') class-attribute instance-attribute
djstripe.models.billing.Subscription.next_pending_invoice_item_invoice = StripeDateTimeField(null=True, blank=True, help_text='Specifies the approximate timestamp on which any pending invoice items will be billed according to the schedule provided at pending_invoice_item_interval.') class-attribute instance-attribute
djstripe.models.billing.Subscription.objects = SubscriptionManager() class-attribute instance-attribute
djstripe.models.billing.Subscription.pause_collection = JSONField(null=True, blank=True, help_text='If specified, payment collection for this subscription will be paused.') class-attribute instance-attribute
djstripe.models.billing.Subscription.pending_invoice_item_interval = JSONField(null=True, blank=True, help_text='Specifies an interval for how often to bill for any pending invoice items. It is analogous to calling Create an invoice for the given subscription at the specified interval.') class-attribute instance-attribute
djstripe.models.billing.Subscription.pending_setup_intent = StripeForeignKey('SetupIntent', null=True, blank=True, on_delete=models.CASCADE, related_name='setup_intents', help_text="We can use this SetupIntent to collect user authentication when creating a subscription without immediate payment or updating a subscription's payment method, allowing you to optimize for off-session payments.") class-attribute instance-attribute
djstripe.models.billing.Subscription.pending_update = JSONField(null=True, blank=True, help_text='If specified, pending updates that will be applied to the subscription once the latest_invoice has been paid.') class-attribute instance-attribute
djstripe.models.billing.Subscription.plan = models.ForeignKey('Plan', null=True, blank=True, on_delete=models.CASCADE, related_name='subscriptions', help_text='The plan associated with this subscription. This value will be `null` for multi-plan subscriptions') class-attribute instance-attribute
djstripe.models.billing.Subscription.proration_behavior = StripeEnumField(enum=enums.SubscriptionProrationBehavior, help_text='Determines how to handle prorations when the billing cycle changes (e.g., when switching plans, resetting billing_cycle_anchor=now, or starting a trial), or if an item’s quantity changes', default=enums.SubscriptionProrationBehavior.create_prorations, blank=True) class-attribute instance-attribute
djstripe.models.billing.Subscription.proration_date = StripeDateTimeField(null=True, blank=True, help_text='If set, the proration will be calculated as though the subscription was updated at the given time. This can be used to apply exactly the same proration that was previewed with upcoming invoice endpoint. It can also be used to implement custom proration logic, such as prorating by day instead of by second, by providing the time that you wish to use for proration calculations') class-attribute instance-attribute
djstripe.models.billing.Subscription.quantity = models.IntegerField(null=True, blank=True, help_text='The quantity applied to this subscription. This value will be `null` for multi-plan subscriptions') class-attribute instance-attribute
djstripe.models.billing.Subscription.schedule = models.ForeignKey('SubscriptionSchedule', null=True, blank=True, on_delete=models.CASCADE, related_name='subscriptions', help_text='The schedule associated with this subscription.') class-attribute instance-attribute
djstripe.models.billing.Subscription.start_date = StripeDateTimeField(null=True, blank=True, help_text='Date when the subscription was first created. The date might differ from the created date due to backdating.') class-attribute instance-attribute
djstripe.models.billing.Subscription.status = StripeEnumField(enum=enums.SubscriptionStatus, help_text='The status of this subscription.') class-attribute instance-attribute
djstripe.models.billing.Subscription.stripe_class = stripe.Subscription class-attribute instance-attribute
djstripe.models.billing.Subscription.stripe_dashboard_item_name = 'subscriptions' class-attribute instance-attribute
djstripe.models.billing.Subscription.trial_end = StripeDateTimeField(null=True, blank=True, help_text='If the subscription has a trial, the end of that trial.') class-attribute instance-attribute
djstripe.models.billing.Subscription.trial_start = StripeDateTimeField(null=True, blank=True, help_text='If the subscription has a trial, the beginning of that trial.') class-attribute instance-attribute

Functions

djstripe.models.billing.Subscription.api_list(api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs) classmethod

Call the stripe API's list operation for this model. :param api_key: The api key to use for this request. Defaults to djstripe_settings.STRIPE_SECRET_KEY. :type api_key: string See Stripe documentation for accepted kwargs for each object. :returns: an iterator over all items in the query

Source code in djstripe/models/billing.py
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
@classmethod
def api_list(cls, api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs):
    """
    Call the stripe API's list operation for this model.
    :param api_key: The api key to use for this request. \
        Defaults to djstripe_settings.STRIPE_SECRET_KEY.
    :type api_key: string
    See Stripe documentation for accepted kwargs for each object.
    :returns: an iterator over all items in the query
    """
    # Update kwargs with `expand` param
    kwargs = cls.get_expand_params(api_key, **kwargs)

    if not kwargs.get("status"):
        # special case: https://stripe.com/docs/api/subscriptions/list#list_subscriptions-status
        # See Issue: https://github.com/dj-stripe/dj-stripe/issues/1763
        kwargs["status"] = "all"
    return super().api_list(api_key=api_key, **kwargs)
djstripe.models.billing.Subscription.cancel(at_period_end=False, **kwargs)

Cancels this subscription. If you set the at_period_end parameter to true, the subscription will remain active until the end of the period, at which point it will be canceled and not renewed. By default, the subscription is terminated immediately. In either case, the customer will not be charged again for the subscription. Note, however, that any pending invoice items or metered usage will still be charged at the end of the period unless manually deleted.

Depending on how proration_behavior is set, any pending prorations will also be left in place and collected at the end of the period. However, if the subscription is set to cancel immediately, you can pass the prorate and invoice_now flags in kwargs to configure how the pending metered usage is invoiced and how proration must work.

By default, all unpaid invoices for the customer will be closed upon subscription cancellation. We do this in order to prevent unexpected payment retries once the customer has canceled a subscription. However, you can reopen the invoices manually after subscription cancellation to have us proceed with automatic retries, or you could even re-attempt payment yourself on all unpaid invoices before allowing the customer to cancel the subscription at all.

:param at_period_end: A flag that if set to true will delay the cancellation of the subscription until the end of the current period. Default is False. :type at_period_end: boolean

.. important:: If a subscription is canceled during a trial period, the at_period_end flag will be overridden to False so that the trial ends immediately and the customer's card isn't charged.

Source code in djstripe/models/billing.py
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
def cancel(self, at_period_end: bool = False, **kwargs):
    """
    Cancels this subscription. If you set the at_period_end parameter to true,
    the subscription will remain active until the end of the period, at which point
    it will be canceled and not renewed. By default, the subscription is terminated
    immediately. In either case, the customer will not be charged again for
    the subscription. Note, however, that any pending invoice items or metered
    usage will still be charged at the end of the period unless manually
    deleted.

    Depending on how `proration_behavior` is set, any pending prorations will
    also be left in place and collected at the end of the period.
    However, if the subscription is set to cancel immediately, you can pass the
    `prorate` and `invoice_now` flags in `kwargs` to configure how the pending
    metered usage is invoiced and how proration must work.

    By default, all unpaid invoices for the customer will be closed upon
    subscription cancellation. We do this in order to prevent unexpected payment
    retries once the customer has canceled a subscription. However, you can
    reopen the invoices manually after subscription cancellation to have us proceed
    with automatic retries, or you could even re-attempt payment yourself on all
    unpaid invoices before allowing the customer to cancel the
    subscription at all.

    :param at_period_end: A flag that if set to true will delay the cancellation \
        of the subscription until the end of the current period. Default is False.
    :type at_period_end: boolean

    .. important:: If a subscription is canceled during a trial period, \
    the ``at_period_end`` flag will be overridden to False so that the trial ends \
    immediately and the customer's card isn't charged.
    """

    # If plan has trial days and customer cancels before
    # trial period ends, then end subscription now,
    # i.e. at_period_end=False
    if self.trial_end and self.trial_end > timezone.now():
        at_period_end = False

    if at_period_end:
        stripe_subscription = self._api_update(cancel_at_period_end=True, **kwargs)
    else:
        try:
            stripe_subscription = self._api_delete(**kwargs)
        except InvalidRequestError as exc:
            if "No such subscription:" in str(exc):
                # cancel() works by deleting the subscription. The object still
                # exists in Stripe however, and can still be retrieved.
                # If the subscription was already canceled (status=canceled),
                # that api_retrieve() call will fail with "No such subscription".
                # However, this may also happen if the subscription legitimately
                # does not exist, in which case the following line will re-raise.
                stripe_subscription = self.api_retrieve()
            else:
                raise

    return Subscription.sync_from_stripe_data(
        stripe_subscription, api_key=self.default_api_key
    )
djstripe.models.billing.Subscription.extend(delta)

Extends this subscription by the provided delta.

:param delta: The timedelta by which to extend this subscription. :type delta: timedelta

Source code in djstripe/models/billing.py
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
def extend(self, delta):
    """
    Extends this subscription by the provided delta.

    :param delta: The timedelta by which to extend this subscription.
    :type delta: timedelta
    """

    if delta.total_seconds() < 0:
        raise ValueError("delta must be a positive timedelta.")

    if self.trial_end is not None and self.trial_end > timezone.now():
        period_end = self.trial_end
    else:
        period_end = self.current_period_end

    period_end += delta

    return self.update(proration_behavior="none", trial_end=period_end)
djstripe.models.billing.Subscription.is_period_current()

Returns True if this subscription's period is current, false otherwise.

Source code in djstripe/models/billing.py
2149
2150
2151
2152
2153
2154
2155
2156
def is_period_current(self):
    """
    Returns True if this subscription's period is current, false otherwise.
    """

    return self.current_period_end > timezone.now() or (
        self.trial_end and self.trial_end > timezone.now()
    )
djstripe.models.billing.Subscription.is_status_current()

Returns True if this subscription's status is current (active or trialing), false otherwise.

Source code in djstripe/models/billing.py
2158
2159
2160
2161
2162
2163
2164
def is_status_current(self):
    """
    Returns True if this subscription's status is current (active or trialing),
    false otherwise.
    """

    return self.status in ["trialing", "active"]
djstripe.models.billing.Subscription.is_status_temporarily_current()

A status is temporarily current when the subscription is canceled with the at_period_end flag. The subscription is still active, but is technically canceled and we're just waiting for it to run out.

You could use this method to give customers limited service after they've canceled. For example, a video on demand service could only allow customers to download their libraries and do nothing else when their subscription is temporarily current.

Source code in djstripe/models/billing.py
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
def is_status_temporarily_current(self):
    """
    A status is temporarily current when the subscription is canceled with the
    ``at_period_end`` flag.
    The subscription is still active, but is technically canceled and we're just
    waiting for it to run out.

    You could use this method to give customers limited service after they've
    canceled. For example, a video on demand service could only allow customers
    to download their libraries and do nothing else when their
    subscription is temporarily current.
    """

    return (
        self.canceled_at
        and self.cancel_at_period_end
        and timezone.now() < self.current_period_end
    )
djstripe.models.billing.Subscription.is_valid()

Returns True if this subscription's status and period are current, false otherwise.

Source code in djstripe/models/billing.py
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
def is_valid(self):
    """
    Returns True if this subscription's status and period are current,
    false otherwise.
    """

    if not self.is_status_current():
        return False

    if not self.is_period_current():
        return False

    return True
djstripe.models.billing.Subscription.reactivate()

Reactivates this subscription.

If a customer's subscription is canceled with at_period_end set to True and it has not yet reached the end of the billing period, it can be reactivated. Subscriptions canceled immediately cannot be reactivated. (Source: https://stripe.com/docs/billing/subscriptions/cancel)

.. warning:: Reactivating a fully canceled Subscription will fail silently. Be sure to check the returned Subscription's status.

Source code in djstripe/models/billing.py
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
def reactivate(self):
    """
    Reactivates this subscription.

    If a customer's subscription is canceled with ``at_period_end`` set to True and
    it has not yet reached the end of the billing period, it can be reactivated.
    Subscriptions canceled immediately cannot be reactivated.
    (Source: https://stripe.com/docs/billing/subscriptions/cancel)

    .. warning:: Reactivating a fully canceled Subscription will fail silently. \
    Be sure to check the returned Subscription's status.
    """
    stripe_subscription = self.api_retrieve()
    stripe_subscription.plan = self.plan.id
    stripe_subscription.cancel_at_period_end = False

    return Subscription.sync_from_stripe_data(stripe_subscription.save())
djstripe.models.billing.Subscription.update(plan=None, **kwargs)

See Customer.subscribe() <#djstripe.models.Customer.subscribe>__

:param plan: The plan to which to subscribe the customer. :type plan: Plan or string (plan ID)

.. important:: Updating a subscription by changing the plan or quantity creates a new Subscription in Stripe (and dj-stripe).

Source code in djstripe/models/billing.py
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
def update(self, plan: Union[StripeModel, str] = None, **kwargs):
    """
    See `Customer.subscribe() <#djstripe.models.Customer.subscribe>`__

    :param plan: The plan to which to subscribe the customer.
    :type plan: Plan or string (plan ID)

    .. important:: Updating a subscription by changing the plan or quantity \
        creates a new ``Subscription`` in \
        Stripe (and dj-stripe).
    """

    # Convert Plan to id
    if plan is not None and isinstance(plan, StripeModel):
        plan = plan.id

    stripe_subscription = self._api_update(plan=plan, **kwargs)

    api_key = kwargs.get("api_key") or self.default_api_key
    return Subscription.sync_from_stripe_data(stripe_subscription, api_key=api_key)

djstripe.models.billing.SubscriptionItem

Bases: StripeModel

Subscription items allow you to create customer subscriptions with more than one plan, making it easy to represent complex billing relationships.

Stripe documentation: https://stripe.com/docs/api?lang=python#subscription_items

Source code in djstripe/models/billing.py
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
class SubscriptionItem(StripeModel):
    """
    Subscription items allow you to create customer subscriptions
    with more than one plan, making it easy to represent complex billing relationships.

    Stripe documentation: https://stripe.com/docs/api?lang=python#subscription_items
    """

    stripe_class = stripe.SubscriptionItem

    billing_thresholds = JSONField(
        null=True,
        blank=True,
        help_text=(
            "Define thresholds at which an invoice will be sent, and the "
            "related subscription advanced to a new billing period."
        ),
    )
    plan = models.ForeignKey(
        "Plan",
        on_delete=models.CASCADE,
        related_name="subscription_items",
        help_text="The plan the customer is subscribed to.",
    )
    price = models.ForeignKey(
        "Price",
        null=True,
        blank=True,
        on_delete=models.CASCADE,
        related_name="subscription_items",
        help_text="The price the customer is subscribed to.",
    )
    proration_behavior = StripeEnumField(
        enum=enums.SubscriptionProrationBehavior,
        help_text=(
            "Determines how to handle prorations when the billing cycle changes (e.g.,"
            " when switching plans, resetting billing_cycle_anchor=now, or starting a"
            " trial), or if an item’s quantity changes"
        ),
        default=enums.SubscriptionProrationBehavior.create_prorations,
        blank=True,
    )
    proration_date = StripeDateTimeField(
        null=True,
        blank=True,
        help_text=(
            "If set, the proration will be calculated as though the subscription was"
            " updated at the given time. This can be used to apply exactly the same"
            " proration that was previewed with upcoming invoice endpoint. It can also"
            " be used to implement custom proration logic, such as prorating by day"
            " instead of by second, by providing the time that you wish to use for"
            " proration calculations"
        ),
    )
    quantity = models.PositiveIntegerField(
        null=True,
        blank=True,
        help_text=(
            "The quantity of the plan to which the customer should be subscribed."
        ),
    )
    subscription = StripeForeignKey(
        "Subscription",
        on_delete=models.CASCADE,
        related_name="items",
        help_text="The subscription this subscription item belongs to.",
    )
    tax_rates = models.ManyToManyField(
        "TaxRate",
        # explicitly specify the joining table name as though the joining model
        # was defined with through="DjstripeSubscriptionItemTaxRate"
        db_table="djstripe_djstripesubscriptionitemtaxrate",
        related_name="+",
        blank=True,
        help_text=(
            "The tax rates which apply to this subscription_item. When set, "
            "the default_tax_rates on the subscription do not apply to this "
            "subscription_item."
        ),
    )

    def _attach_objects_post_save_hook(
        self,
        cls,
        data,
        api_key=djstripe_settings.STRIPE_SECRET_KEY,
        pending_relations=None,
    ):
        super()._attach_objects_post_save_hook(
            cls, data, api_key=api_key, pending_relations=pending_relations
        )

        self.tax_rates.set(
            cls._stripe_object_to_tax_rates(
                target_cls=TaxRate, data=data, api_key=api_key
            )
        )

Attributes

djstripe.models.billing.SubscriptionItem.billing_thresholds = JSONField(null=True, blank=True, help_text='Define thresholds at which an invoice will be sent, and the related subscription advanced to a new billing period.') class-attribute instance-attribute
djstripe.models.billing.SubscriptionItem.plan = models.ForeignKey('Plan', on_delete=models.CASCADE, related_name='subscription_items', help_text='The plan the customer is subscribed to.') class-attribute instance-attribute
djstripe.models.billing.SubscriptionItem.price = models.ForeignKey('Price', null=True, blank=True, on_delete=models.CASCADE, related_name='subscription_items', help_text='The price the customer is subscribed to.') class-attribute instance-attribute
djstripe.models.billing.SubscriptionItem.proration_behavior = StripeEnumField(enum=enums.SubscriptionProrationBehavior, help_text='Determines how to handle prorations when the billing cycle changes (e.g., when switching plans, resetting billing_cycle_anchor=now, or starting a trial), or if an item’s quantity changes', default=enums.SubscriptionProrationBehavior.create_prorations, blank=True) class-attribute instance-attribute
djstripe.models.billing.SubscriptionItem.proration_date = StripeDateTimeField(null=True, blank=True, help_text='If set, the proration will be calculated as though the subscription was updated at the given time. This can be used to apply exactly the same proration that was previewed with upcoming invoice endpoint. It can also be used to implement custom proration logic, such as prorating by day instead of by second, by providing the time that you wish to use for proration calculations') class-attribute instance-attribute
djstripe.models.billing.SubscriptionItem.quantity = models.PositiveIntegerField(null=True, blank=True, help_text='The quantity of the plan to which the customer should be subscribed.') class-attribute instance-attribute
djstripe.models.billing.SubscriptionItem.stripe_class = stripe.SubscriptionItem class-attribute instance-attribute
djstripe.models.billing.SubscriptionItem.subscription = StripeForeignKey('Subscription', on_delete=models.CASCADE, related_name='items', help_text='The subscription this subscription item belongs to.') class-attribute instance-attribute
djstripe.models.billing.SubscriptionItem.tax_rates = models.ManyToManyField('TaxRate', db_table='djstripe_djstripesubscriptionitemtaxrate', related_name='+', blank=True, help_text='The tax rates which apply to this subscription_item. When set, the default_tax_rates on the subscription do not apply to this subscription_item.') class-attribute instance-attribute

djstripe.models.billing.SubscriptionSchedule

Bases: StripeModel

Subscription schedules allow you to create and manage the lifecycle of a subscription by predefining expected changes.

Stripe documentation: https://stripe.com/docs/api/subscription_schedules?lang=python

Source code in djstripe/models/billing.py
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
class SubscriptionSchedule(StripeModel):
    """
    Subscription schedules allow you to create and manage the lifecycle
    of a subscription by predefining expected changes.

    Stripe documentation: https://stripe.com/docs/api/subscription_schedules?lang=python
    """

    stripe_class = stripe.SubscriptionSchedule
    stripe_dashboard_item_name = "subscription_schedules"

    canceled_at = StripeDateTimeField(
        null=True,
        blank=True,
        help_text="Time at which the subscription schedule was canceled.",
    )
    completed_at = StripeDateTimeField(
        null=True,
        blank=True,
        help_text="Time at which the subscription schedule was completed.",
    )
    current_phase = JSONField(
        null=True,
        blank=True,
        help_text=(
            "Object representing the start and end dates for the "
            "current phase of the subscription schedule, if it is `active`."
        ),
    )
    customer = models.ForeignKey(
        "Customer",
        on_delete=models.CASCADE,
        related_name="schedules",
        help_text="The customer who owns the subscription schedule.",
    )
    default_settings = JSONField(
        null=True,
        blank=True,
        help_text="Object representing the subscription schedule's default settings.",
    )
    end_behavior = StripeEnumField(
        enum=enums.SubscriptionScheduleEndBehavior,
        help_text=(
            "Behavior of the subscription schedule and underlying "
            "subscription when it ends."
        ),
    )
    phases = JSONField(
        null=True,
        blank=True,
        help_text="Configuration for the subscription schedule's phases.",
    )
    released_at = StripeDateTimeField(
        null=True,
        blank=True,
        help_text="Time at which the subscription schedule was released.",
    )
    released_subscription = models.ForeignKey(
        "Subscription",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="released_schedules",
        help_text=(
            "The subscription once managed by this subscription schedule "
            "(if it is released)."
        ),
    )
    status = StripeEnumField(
        enum=enums.SubscriptionScheduleStatus,
        help_text=(
            "The present status of the subscription schedule. Possible "
            "values are `not_started`, `active`, `completed`, `released`, and "
            "`canceled`."
        ),
    )
    subscription = models.ForeignKey(
        "Subscription",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="subscriptions",
        help_text="ID of the subscription managed by the subscription schedule.",
    )

    def release(self, api_key=None, stripe_account=None, **kwargs):
        """
        Releases the subscription schedule immediately, which will stop scheduling
        of its phases, but leave any existing subscription in place.
        A schedule can only be released if its status is not_started or active.
        If the subscription schedule is currently associated with a subscription,
        releasing it will remove its subscription property and set the subscription’s
        ID to the released_subscription property
        and returns the Released SubscriptionSchedule.
        :param api_key: The api key to use for this request.
            Defaults to djstripe_settings.STRIPE_SECRET_KEY.
        :type api_key: string
        :param stripe_account: The optional connected account \
            for which this request is being made.
        :type stripe_account: string
        """

        api_key = api_key or self.default_api_key

        # Prefer passed in stripe_account if set.
        if not stripe_account:
            stripe_account = self._get_stripe_account_id(api_key)

        stripe_subscription_schedule = self.stripe_class.release(
            self.id,
            api_key=api_key,
            stripe_account=stripe_account,
            stripe_version=djstripe_settings.STRIPE_API_VERSION,
            **kwargs,
        )

        return SubscriptionSchedule.sync_from_stripe_data(stripe_subscription_schedule)

    def cancel(self, api_key=None, stripe_account=None, **kwargs):
        """
        Cancels a subscription schedule and its associated subscription immediately
        (if the subscription schedule has an active subscription). A subscription schedule can only be canceled if its status is not_started or active
        and returns the Canceled SubscriptionSchedule.
        :param api_key: The api key to use for this request.
            Defaults to djstripe_settings.STRIPE_SECRET_KEY.
        :type api_key: string
        :param stripe_account: The optional connected account \
            for which this request is being made.
        :type stripe_account: string
        """

        api_key = api_key or self.default_api_key

        # Prefer passed in stripe_account if set.
        if not stripe_account:
            stripe_account = self._get_stripe_account_id(api_key)

        stripe_subscription_schedule = self.stripe_class.cancel(
            self.id,
            api_key=api_key,
            stripe_account=stripe_account,
            stripe_version=djstripe_settings.STRIPE_API_VERSION,
            **kwargs,
        )

        return SubscriptionSchedule.sync_from_stripe_data(stripe_subscription_schedule)

    def update(self, api_key=None, stripe_account=None, **kwargs):
        """
        Updates an existing subscription schedule
        and returns the updated SubscriptionSchedule.
        :param api_key: The api key to use for this request.
            Defaults to djstripe_settings.STRIPE_SECRET_KEY.
        :type api_key: string
        :param stripe_account: The optional connected account \
            for which this request is being made.
        :type stripe_account: string
        """
        stripe_subscription_schedule = self._api_update(
            api_key=api_key, stripe_account=stripe_account, **kwargs
        )
        return SubscriptionSchedule.sync_from_stripe_data(stripe_subscription_schedule)

Attributes

djstripe.models.billing.SubscriptionSchedule.canceled_at = StripeDateTimeField(null=True, blank=True, help_text='Time at which the subscription schedule was canceled.') class-attribute instance-attribute
djstripe.models.billing.SubscriptionSchedule.completed_at = StripeDateTimeField(null=True, blank=True, help_text='Time at which the subscription schedule was completed.') class-attribute instance-attribute
djstripe.models.billing.SubscriptionSchedule.current_phase = JSONField(null=True, blank=True, help_text='Object representing the start and end dates for the current phase of the subscription schedule, if it is `active`.') class-attribute instance-attribute
djstripe.models.billing.SubscriptionSchedule.customer = models.ForeignKey('Customer', on_delete=models.CASCADE, related_name='schedules', help_text='The customer who owns the subscription schedule.') class-attribute instance-attribute
djstripe.models.billing.SubscriptionSchedule.default_settings = JSONField(null=True, blank=True, help_text="Object representing the subscription schedule's default settings.") class-attribute instance-attribute
djstripe.models.billing.SubscriptionSchedule.end_behavior = StripeEnumField(enum=enums.SubscriptionScheduleEndBehavior, help_text='Behavior of the subscription schedule and underlying subscription when it ends.') class-attribute instance-attribute
djstripe.models.billing.SubscriptionSchedule.phases = JSONField(null=True, blank=True, help_text="Configuration for the subscription schedule's phases.") class-attribute instance-attribute
djstripe.models.billing.SubscriptionSchedule.released_at = StripeDateTimeField(null=True, blank=True, help_text='Time at which the subscription schedule was released.') class-attribute instance-attribute
djstripe.models.billing.SubscriptionSchedule.released_subscription = models.ForeignKey('Subscription', null=True, blank=True, on_delete=models.SET_NULL, related_name='released_schedules', help_text='The subscription once managed by this subscription schedule (if it is released).') class-attribute instance-attribute
djstripe.models.billing.SubscriptionSchedule.status = StripeEnumField(enum=enums.SubscriptionScheduleStatus, help_text='The present status of the subscription schedule. Possible values are `not_started`, `active`, `completed`, `released`, and `canceled`.') class-attribute instance-attribute
djstripe.models.billing.SubscriptionSchedule.stripe_class = stripe.SubscriptionSchedule class-attribute instance-attribute
djstripe.models.billing.SubscriptionSchedule.stripe_dashboard_item_name = 'subscription_schedules' class-attribute instance-attribute
djstripe.models.billing.SubscriptionSchedule.subscription = models.ForeignKey('Subscription', null=True, blank=True, on_delete=models.SET_NULL, related_name='subscriptions', help_text='ID of the subscription managed by the subscription schedule.') class-attribute instance-attribute

Functions

djstripe.models.billing.SubscriptionSchedule.cancel(api_key=None, stripe_account=None, **kwargs)

Cancels a subscription schedule and its associated subscription immediately (if the subscription schedule has an active subscription). A subscription schedule can only be canceled if its status is not_started or active and returns the Canceled SubscriptionSchedule. :param api_key: The api key to use for this request. Defaults to djstripe_settings.STRIPE_SECRET_KEY. :type api_key: string :param stripe_account: The optional connected account for which this request is being made. :type stripe_account: string

Source code in djstripe/models/billing.py
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
def cancel(self, api_key=None, stripe_account=None, **kwargs):
    """
    Cancels a subscription schedule and its associated subscription immediately
    (if the subscription schedule has an active subscription). A subscription schedule can only be canceled if its status is not_started or active
    and returns the Canceled SubscriptionSchedule.
    :param api_key: The api key to use for this request.
        Defaults to djstripe_settings.STRIPE_SECRET_KEY.
    :type api_key: string
    :param stripe_account: The optional connected account \
        for which this request is being made.
    :type stripe_account: string
    """

    api_key = api_key or self.default_api_key

    # Prefer passed in stripe_account if set.
    if not stripe_account:
        stripe_account = self._get_stripe_account_id(api_key)

    stripe_subscription_schedule = self.stripe_class.cancel(
        self.id,
        api_key=api_key,
        stripe_account=stripe_account,
        stripe_version=djstripe_settings.STRIPE_API_VERSION,
        **kwargs,
    )

    return SubscriptionSchedule.sync_from_stripe_data(stripe_subscription_schedule)
djstripe.models.billing.SubscriptionSchedule.release(api_key=None, stripe_account=None, **kwargs)

Releases the subscription schedule immediately, which will stop scheduling of its phases, but leave any existing subscription in place. A schedule can only be released if its status is not_started or active. If the subscription schedule is currently associated with a subscription, releasing it will remove its subscription property and set the subscription’s ID to the released_subscription property and returns the Released SubscriptionSchedule. :param api_key: The api key to use for this request. Defaults to djstripe_settings.STRIPE_SECRET_KEY. :type api_key: string :param stripe_account: The optional connected account for which this request is being made. :type stripe_account: string

Source code in djstripe/models/billing.py
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
def release(self, api_key=None, stripe_account=None, **kwargs):
    """
    Releases the subscription schedule immediately, which will stop scheduling
    of its phases, but leave any existing subscription in place.
    A schedule can only be released if its status is not_started or active.
    If the subscription schedule is currently associated with a subscription,
    releasing it will remove its subscription property and set the subscription’s
    ID to the released_subscription property
    and returns the Released SubscriptionSchedule.
    :param api_key: The api key to use for this request.
        Defaults to djstripe_settings.STRIPE_SECRET_KEY.
    :type api_key: string
    :param stripe_account: The optional connected account \
        for which this request is being made.
    :type stripe_account: string
    """

    api_key = api_key or self.default_api_key

    # Prefer passed in stripe_account if set.
    if not stripe_account:
        stripe_account = self._get_stripe_account_id(api_key)

    stripe_subscription_schedule = self.stripe_class.release(
        self.id,
        api_key=api_key,
        stripe_account=stripe_account,
        stripe_version=djstripe_settings.STRIPE_API_VERSION,
        **kwargs,
    )

    return SubscriptionSchedule.sync_from_stripe_data(stripe_subscription_schedule)
djstripe.models.billing.SubscriptionSchedule.update(api_key=None, stripe_account=None, **kwargs)

Updates an existing subscription schedule and returns the updated SubscriptionSchedule. :param api_key: The api key to use for this request. Defaults to djstripe_settings.STRIPE_SECRET_KEY. :type api_key: string :param stripe_account: The optional connected account for which this request is being made. :type stripe_account: string

Source code in djstripe/models/billing.py
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
def update(self, api_key=None, stripe_account=None, **kwargs):
    """
    Updates an existing subscription schedule
    and returns the updated SubscriptionSchedule.
    :param api_key: The api key to use for this request.
        Defaults to djstripe_settings.STRIPE_SECRET_KEY.
    :type api_key: string
    :param stripe_account: The optional connected account \
        for which this request is being made.
    :type stripe_account: string
    """
    stripe_subscription_schedule = self._api_update(
        api_key=api_key, stripe_account=stripe_account, **kwargs
    )
    return SubscriptionSchedule.sync_from_stripe_data(stripe_subscription_schedule)

djstripe.models.billing.TaxCode

Bases: StripeModel

Tax codes classify goods and services for tax purposes.

Stripe documentation: https://stripe.com/docs/api/tax_codes

Source code in djstripe/models/billing.py
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
class TaxCode(StripeModel):
    """
    Tax codes classify goods and services for tax purposes.

    Stripe documentation: https://stripe.com/docs/api/tax_codes
    """

    stripe_class = stripe.TaxCode
    metadata = None

    name = models.CharField(
        max_length=128,
        help_text="A short name for the tax code.",
    )

    class Meta(StripeModel.Meta):
        verbose_name = "Tax Code"

    def __str__(self):
        return f"{self.name}: {self.id}"

    @classmethod
    def _find_owner_account(cls, data, api_key=djstripe_settings.STRIPE_SECRET_KEY):
        # Tax Codes do not belong to any Stripe Account
        pass

Attributes

djstripe.models.billing.TaxCode.metadata = None class-attribute instance-attribute
djstripe.models.billing.TaxCode.name = models.CharField(max_length=128, help_text='A short name for the tax code.') class-attribute instance-attribute
djstripe.models.billing.TaxCode.stripe_class = stripe.TaxCode class-attribute instance-attribute

Classes

djstripe.models.billing.TaxCode.Meta

Bases: Meta

Source code in djstripe/models/billing.py
2577
2578
class Meta(StripeModel.Meta):
    verbose_name = "Tax Code"
Attributes
djstripe.models.billing.TaxCode.Meta.verbose_name = 'Tax Code' class-attribute instance-attribute

Functions

djstripe.models.billing.TaxCode.__str__()
Source code in djstripe/models/billing.py
2580
2581
def __str__(self):
    return f"{self.name}: {self.id}"

djstripe.models.billing.TaxId

Bases: StripeModel

Add one or multiple tax IDs to a customer. A customer's tax IDs are displayed on invoices and credit notes issued for the customer.

Stripe documentation: https://stripe.com/docs/api/customer_tax_ids?lang=python

Source code in djstripe/models/billing.py
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
class TaxId(StripeModel):
    """
    Add one or multiple tax IDs to a customer.
    A customer's tax IDs are displayed on invoices and
    credit notes issued for the customer.

    Stripe documentation: https://stripe.com/docs/api/customer_tax_ids?lang=python
    """

    stripe_class = stripe.TaxId
    description = None
    metadata = None

    country = models.CharField(
        max_length=2,
        help_text="Two-letter ISO code representing the country of the tax ID.",
    )
    customer = StripeForeignKey(
        "djstripe.customer", on_delete=models.CASCADE, related_name="tax_ids"
    )
    type = StripeEnumField(
        enum=enums.TaxIdType, help_text="The status of this subscription."
    )
    value = models.CharField(max_length=50, help_text="Value of the tax ID.")
    verification = JSONField(help_text="Tax ID verification information.")

    def __str__(self):
        return f"{enums.TaxIdType.humanize(self.type)} {self.value} ({self.verification.get('status')})"

    class Meta(StripeModel.Meta):
        verbose_name = "Tax ID"

    @classmethod
    def _api_create(cls, api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs):
        """
        Call the stripe API's create operation for this model.

        :param api_key: The api key to use for this request. \
            Defaults to djstripe_settings.STRIPE_SECRET_KEY.
        :type api_key: string
        """

        if not kwargs.get("id"):
            raise KeyError("Customer Object ID is missing")

        try:
            Customer.objects.get(id=kwargs["id"])
        except Customer.DoesNotExist:
            raise

        return stripe.Customer.create_tax_id(api_key=api_key, **kwargs)

    def api_retrieve(self, api_key=None, stripe_account=None):
        """
        Call the stripe API's retrieve operation for this model.
        :param api_key: The api key to use for this request. \
            Defaults to djstripe_settings.STRIPE_SECRET_KEY.
        :type api_key: string
        :param stripe_account: The optional connected account \
            for which this request is being made.
        :type stripe_account: string
        """
        nested_id = self.id
        id = self.customer.id
        api_key = api_key or self.default_api_key

        # Prefer passed in stripe_account if set.
        if not stripe_account:
            stripe_account = self._get_stripe_account_id(api_key)

        return stripe.Customer.retrieve_tax_id(
            id=id,
            nested_id=nested_id,
            api_key=api_key,
            expand=self.expand_fields,
            stripe_account=stripe_account,
            stripe_version=djstripe_settings.STRIPE_API_VERSION,
        )

    @classmethod
    def api_list(cls, api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs):
        """
        Call the stripe API's list operation for this model.
        :param api_key: The api key to use for this request. \
            Defaults to djstripe_settings.STRIPE_SECRET_KEY.
        :type api_key: string
        See Stripe documentation for accepted kwargs for each object.
        :returns: an iterator over all items in the query
        """
        # Update kwargs with `expand` param
        kwargs = cls.get_expand_params(api_key, **kwargs)

        return stripe.Customer.list_tax_ids(
            api_key=api_key,
            stripe_version=djstripe_settings.STRIPE_API_VERSION,
            **kwargs,
        ).auto_paging_iter()

Attributes

djstripe.models.billing.TaxId.country = models.CharField(max_length=2, help_text='Two-letter ISO code representing the country of the tax ID.') class-attribute instance-attribute
djstripe.models.billing.TaxId.customer = StripeForeignKey('djstripe.customer', on_delete=models.CASCADE, related_name='tax_ids') class-attribute instance-attribute
djstripe.models.billing.TaxId.description = None class-attribute instance-attribute
djstripe.models.billing.TaxId.metadata = None class-attribute instance-attribute
djstripe.models.billing.TaxId.stripe_class = stripe.TaxId class-attribute instance-attribute
djstripe.models.billing.TaxId.type = StripeEnumField(enum=enums.TaxIdType, help_text='The status of this subscription.') class-attribute instance-attribute
djstripe.models.billing.TaxId.value = models.CharField(max_length=50, help_text='Value of the tax ID.') class-attribute instance-attribute
djstripe.models.billing.TaxId.verification = JSONField(help_text='Tax ID verification information.') class-attribute instance-attribute

Classes

djstripe.models.billing.TaxId.Meta

Bases: Meta

Source code in djstripe/models/billing.py
2618
2619
class Meta(StripeModel.Meta):
    verbose_name = "Tax ID"
Attributes
djstripe.models.billing.TaxId.Meta.verbose_name = 'Tax ID' class-attribute instance-attribute

Functions

djstripe.models.billing.TaxId.__str__()
Source code in djstripe/models/billing.py
2615
2616
def __str__(self):
    return f"{enums.TaxIdType.humanize(self.type)} {self.value} ({self.verification.get('status')})"
djstripe.models.billing.TaxId.api_list(api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs) classmethod

Call the stripe API's list operation for this model. :param api_key: The api key to use for this request. Defaults to djstripe_settings.STRIPE_SECRET_KEY. :type api_key: string See Stripe documentation for accepted kwargs for each object. :returns: an iterator over all items in the query

Source code in djstripe/models/billing.py
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
@classmethod
def api_list(cls, api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs):
    """
    Call the stripe API's list operation for this model.
    :param api_key: The api key to use for this request. \
        Defaults to djstripe_settings.STRIPE_SECRET_KEY.
    :type api_key: string
    See Stripe documentation for accepted kwargs for each object.
    :returns: an iterator over all items in the query
    """
    # Update kwargs with `expand` param
    kwargs = cls.get_expand_params(api_key, **kwargs)

    return stripe.Customer.list_tax_ids(
        api_key=api_key,
        stripe_version=djstripe_settings.STRIPE_API_VERSION,
        **kwargs,
    ).auto_paging_iter()
djstripe.models.billing.TaxId.api_retrieve(api_key=None, stripe_account=None)

Call the stripe API's retrieve operation for this model. :param api_key: The api key to use for this request. Defaults to djstripe_settings.STRIPE_SECRET_KEY. :type api_key: string :param stripe_account: The optional connected account for which this request is being made. :type stripe_account: string

Source code in djstripe/models/billing.py
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
def api_retrieve(self, api_key=None, stripe_account=None):
    """
    Call the stripe API's retrieve operation for this model.
    :param api_key: The api key to use for this request. \
        Defaults to djstripe_settings.STRIPE_SECRET_KEY.
    :type api_key: string
    :param stripe_account: The optional connected account \
        for which this request is being made.
    :type stripe_account: string
    """
    nested_id = self.id
    id = self.customer.id
    api_key = api_key or self.default_api_key

    # Prefer passed in stripe_account if set.
    if not stripe_account:
        stripe_account = self._get_stripe_account_id(api_key)

    return stripe.Customer.retrieve_tax_id(
        id=id,
        nested_id=nested_id,
        api_key=api_key,
        expand=self.expand_fields,
        stripe_account=stripe_account,
        stripe_version=djstripe_settings.STRIPE_API_VERSION,
    )

djstripe.models.billing.TaxRate

Bases: StripeModel

Tax rates can be applied to invoices and subscriptions to collect tax.

Stripe documentation: https://stripe.com/docs/api/tax_rates?lang=python

Source code in djstripe/models/billing.py
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
class TaxRate(StripeModel):
    """
    Tax rates can be applied to invoices and subscriptions to collect tax.

    Stripe documentation: https://stripe.com/docs/api/tax_rates?lang=python
    """

    stripe_class = stripe.TaxRate
    stripe_dashboard_item_name = "tax-rates"

    active = models.BooleanField(
        default=True,
        help_text=(
            "Defaults to true. When set to false, this tax rate cannot be "
            "applied to objects in the API, but will still be applied to subscriptions "
            "and invoices that already have it set."
        ),
    )
    country = models.CharField(
        max_length=2,
        default="",
        blank=True,
        help_text="Two-letter country code.",
    )
    display_name = models.CharField(
        max_length=50,
        default="",
        blank=True,
        help_text=(
            "The display name of the tax rates as it will appear to your "
            "customer on their receipt email, PDF, and the hosted invoice page."
        ),
    )
    inclusive = models.BooleanField(
        help_text="This specifies if the tax rate is inclusive or exclusive."
    )
    jurisdiction = models.CharField(
        max_length=50,
        default="",
        blank=True,
        help_text="The jurisdiction for the tax rate.",
    )
    percentage = StripePercentField(
        decimal_places=4,
        max_digits=7,
        help_text="This represents the tax rate percent out of 100.",
    )
    state = models.CharField(
        max_length=2,
        default="",
        blank=True,
        help_text="ISO 3166-2 subdivision code, without country prefix.",
    )
    tax_type = models.CharField(
        default="",
        blank=True,
        max_length=50,
        help_text="The high-level tax type, such as vat, gst, sales_tax or custom.",
    )

    def __str__(self):
        return f"{self.display_name} at {self.percentage}%"

    class Meta(StripeModel.Meta):
        verbose_name = "Tax Rate"

Attributes

djstripe.models.billing.TaxRate.active = models.BooleanField(default=True, help_text='Defaults to true. When set to false, this tax rate cannot be applied to objects in the API, but will still be applied to subscriptions and invoices that already have it set.') class-attribute instance-attribute
djstripe.models.billing.TaxRate.country = models.CharField(max_length=2, default='', blank=True, help_text='Two-letter country code.') class-attribute instance-attribute
djstripe.models.billing.TaxRate.display_name = models.CharField(max_length=50, default='', blank=True, help_text='The display name of the tax rates as it will appear to your customer on their receipt email, PDF, and the hosted invoice page.') class-attribute instance-attribute
djstripe.models.billing.TaxRate.inclusive = models.BooleanField(help_text='This specifies if the tax rate is inclusive or exclusive.') class-attribute instance-attribute
djstripe.models.billing.TaxRate.jurisdiction = models.CharField(max_length=50, default='', blank=True, help_text='The jurisdiction for the tax rate.') class-attribute instance-attribute
djstripe.models.billing.TaxRate.percentage = StripePercentField(decimal_places=4, max_digits=7, help_text='This represents the tax rate percent out of 100.') class-attribute instance-attribute
djstripe.models.billing.TaxRate.state = models.CharField(max_length=2, default='', blank=True, help_text='ISO 3166-2 subdivision code, without country prefix.') class-attribute instance-attribute
djstripe.models.billing.TaxRate.stripe_class = stripe.TaxRate class-attribute instance-attribute
djstripe.models.billing.TaxRate.stripe_dashboard_item_name = 'tax-rates' class-attribute instance-attribute
djstripe.models.billing.TaxRate.tax_type = models.CharField(default='', blank=True, max_length=50, help_text='The high-level tax type, such as vat, gst, sales_tax or custom.') class-attribute instance-attribute

Classes

djstripe.models.billing.TaxRate.Meta

Bases: Meta

Source code in djstripe/models/billing.py
2751
2752
class Meta(StripeModel.Meta):
    verbose_name = "Tax Rate"
Attributes
djstripe.models.billing.TaxRate.Meta.verbose_name = 'Tax Rate' class-attribute instance-attribute

Functions

djstripe.models.billing.TaxRate.__str__()
Source code in djstripe/models/billing.py
2748
2749
def __str__(self):
    return f"{self.display_name} at {self.percentage}%"

djstripe.models.billing.UpcomingInvoice

Bases: BaseInvoice

The preview of an upcoming invoice - does not exist in the Django database.

See BaseInvoice.upcoming()

Logically it should be set abstract, but that doesn't quite work since we do actually want to instantiate the model and use relations.

Source code in djstripe/models/billing.py
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
class UpcomingInvoice(BaseInvoice):
    """
    The preview of an upcoming invoice - does not exist in the Django database.

    See BaseInvoice.upcoming()

    Logically it should be set abstract, but that doesn't quite work since we
    do actually want to instantiate the model and use relations.
    """

    default_source = PaymentMethodForeignKey(
        on_delete=models.SET_NULL,
        null=True,
        related_name="upcoming_invoices",
        help_text=(
            "The default payment source for the invoice. "
            "It must belong to the customer associated with the invoice and be "
            "in a chargeable state. If not set, defaults to the subscription's "
            "default source, if any, or to the customer's default source."
        ),
    )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self._lineitems = []
        self._default_tax_rates = []
        self._total_tax_amounts = []

    def get_stripe_dashboard_url(self):
        return ""

    def _attach_objects_hook(
        self, cls, data, api_key=djstripe_settings.STRIPE_SECRET_KEY, current_ids=None
    ):
        super()._attach_objects_hook(
            cls, data, api_key=api_key, current_ids=current_ids
        )

        self._lineitems = cls._stripe_object_to_line_items(
            target_cls=LineItem, data=data, invoice=self, api_key=api_key
        )

    def _attach_objects_post_save_hook(
        self,
        cls,
        data,
        api_key=djstripe_settings.STRIPE_SECRET_KEY,
        pending_relations=None,
    ):
        super()._attach_objects_post_save_hook(
            cls, data, api_key=api_key, pending_relations=pending_relations
        )

        self._default_tax_rates = cls._stripe_object_to_default_tax_rates(
            target_cls=TaxRate, data=data, api_key=api_key
        )

        total_tax_amounts = []

        for tax_amount_data in data.get("total_tax_amounts", []):
            tax_rate_id = tax_amount_data["tax_rate"]
            if not isinstance(tax_rate_id, str):
                tax_rate_id = tax_rate_id["tax_rate"]

            tax_rate = TaxRate._get_or_retrieve(id=tax_rate_id, api_key=api_key)

            tax_amount = DjstripeUpcomingInvoiceTotalTaxAmount(
                invoice=self,
                amount=tax_amount_data["amount"],
                inclusive=tax_amount_data["inclusive"],
                tax_rate=tax_rate,
            )

            total_tax_amounts.append(tax_amount)

        self._total_tax_amounts = total_tax_amounts

    @property
    def invoiceitems(self):
        """
        Gets the invoice items associated with this upcoming invoice.

        This differs from normal (non-upcoming) invoices, in that upcoming
        invoices are in-memory and do not persist to the database. Therefore,
        all of the data comes from the Stripe API itself.

        Instead of returning a normal queryset for the invoiceitems, this will
        return a mock of a queryset, but with the data fetched from Stripe - It
        will act like a normal queryset, but mutation will silently fail.
        """
        # filter lineitems with type="invoice_item" and fetch all the actual InvoiceItem objects
        items = []
        for item in self._lineitems:
            if item.type == "invoice_item":
                items.append(item.invoice_item)

        return QuerySetMock.from_iterable(InvoiceItem, items)

    @property
    def lineitems(self):
        """
        Gets the line items associated with this upcoming invoice.

        This differs from normal (non-upcoming) invoices, in that upcoming
        invoices are in-memory and do not persist to the database. Therefore,
        all of the data comes from the Stripe API itself.

        Instead of returning a normal queryset for the lineitems, this will
        return a mock of a queryset, but with the data fetched from Stripe - It
        will act like a normal queryset, but mutation will silently fail.
        """
        return QuerySetMock.from_iterable(LineItem, self._lineitems)

    @property
    def default_tax_rates(self):
        """
        Gets the default tax rates associated with this upcoming invoice.
        :return:
        """
        return QuerySetMock.from_iterable(TaxRate, self._default_tax_rates)

    @property
    def total_tax_amounts(self):
        """
        Gets the total tax amounts associated with this upcoming invoice.
        :return:
        """
        return QuerySetMock.from_iterable(
            DjstripeUpcomingInvoiceTotalTaxAmount, self._total_tax_amounts
        )

    @property
    def id(self):
        return None

    @id.setter
    def id(self, value):
        return  # noop

    def save(self, *args, **kwargs):
        return  # noop

Attributes

djstripe.models.billing.UpcomingInvoice.default_source = PaymentMethodForeignKey(on_delete=models.SET_NULL, null=True, related_name='upcoming_invoices', help_text="The default payment source for the invoice. It must belong to the customer associated with the invoice and be in a chargeable state. If not set, defaults to the subscription's default source, if any, or to the customer's default source.") class-attribute instance-attribute
djstripe.models.billing.UpcomingInvoice.default_tax_rates property

Gets the default tax rates associated with this upcoming invoice. :return:

djstripe.models.billing.UpcomingInvoice.id property writable
djstripe.models.billing.UpcomingInvoice.invoiceitems property

Gets the invoice items associated with this upcoming invoice.

This differs from normal (non-upcoming) invoices, in that upcoming invoices are in-memory and do not persist to the database. Therefore, all of the data comes from the Stripe API itself.

Instead of returning a normal queryset for the invoiceitems, this will return a mock of a queryset, but with the data fetched from Stripe - It will act like a normal queryset, but mutation will silently fail.

djstripe.models.billing.UpcomingInvoice.lineitems property

Gets the line items associated with this upcoming invoice.

This differs from normal (non-upcoming) invoices, in that upcoming invoices are in-memory and do not persist to the database. Therefore, all of the data comes from the Stripe API itself.

Instead of returning a normal queryset for the lineitems, this will return a mock of a queryset, but with the data fetched from Stripe - It will act like a normal queryset, but mutation will silently fail.

djstripe.models.billing.UpcomingInvoice.total_tax_amounts property

Gets the total tax amounts associated with this upcoming invoice. :return:

Functions

djstripe.models.billing.UpcomingInvoice.__init__(*args, **kwargs)
Source code in djstripe/models/billing.py
963
964
965
966
967
968
def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)

    self._lineitems = []
    self._default_tax_rates = []
    self._total_tax_amounts = []
djstripe.models.billing.UpcomingInvoice.get_stripe_dashboard_url()
Source code in djstripe/models/billing.py
970
971
def get_stripe_dashboard_url(self):
    return ""
djstripe.models.billing.UpcomingInvoice.save(*args, **kwargs)
Source code in djstripe/models/billing.py
1081
1082
def save(self, *args, **kwargs):
    return  # noop

djstripe.models.billing.UsageRecord

Bases: StripeModel

Usage records allow you to continually report usage and metrics to Stripe for metered billing of plans.

Stripe documentation: https://stripe.com/docs/api?lang=python#usage_records

Source code in djstripe/models/billing.py
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
class UsageRecord(StripeModel):
    """
    Usage records allow you to continually report usage and metrics to
    Stripe for metered billing of plans.

    Stripe documentation: https://stripe.com/docs/api?lang=python#usage_records
    """

    description = None
    metadata = None

    stripe_class = stripe.UsageRecord

    quantity = models.PositiveIntegerField(
        help_text="The quantity of the plan to which the customer should be subscribed."
    )
    subscription_item = StripeForeignKey(
        "SubscriptionItem",
        on_delete=models.CASCADE,
        related_name="usage_records",
        help_text="The subscription item this usage record contains data for.",
    )

    timestamp = StripeDateTimeField(
        null=True,
        blank=True,
        help_text=(
            "The timestamp for the usage event. This timestamp must be within the"
            " current billing period of the subscription of the provided"
            " subscription_item."
        ),
    )

    action = StripeEnumField(
        enum=enums.UsageAction,
        default=enums.UsageAction.increment,
        help_text=(
            "When using increment the specified quantity will be added to the usage at"
            " the specified timestamp. The set action will overwrite the usage quantity"
            " at that timestamp. If the subscription has billing thresholds, increment"
            " is the only allowed value."
        ),
    )

    def __str__(self):
        return f"Usage for {self.subscription_item} ({self.action}) is {self.quantity}"

    @classmethod
    def _api_create(cls, api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs):
        """
        Call the stripe API's create operation for this model.

        :param api_key: The api key to use for this request. \
            Defaults to djstripe_settings.STRIPE_SECRET_KEY.
        :type api_key: string
        """

        if not kwargs.get("id"):
            raise KeyError("SubscriptionItem Object ID is missing")

        try:
            SubscriptionItem.objects.get(id=kwargs["id"])
        except SubscriptionItem.DoesNotExist:
            raise

        usage_stripe_data = stripe.SubscriptionItem.create_usage_record(
            api_key=api_key, **kwargs
        )

        # ! Hack: there is no way to retrieve a UsageRecord object from Stripe,
        # ! which is why we create and sync it right here
        cls.sync_from_stripe_data(usage_stripe_data, api_key=api_key)

        return usage_stripe_data

    @classmethod
    def create(cls, **kwargs):
        """
        A wrapper around _api_create() to allow one to create and sync UsageRecord Objects
        """
        return cls._api_create(**kwargs)

Attributes

djstripe.models.billing.UsageRecord.action = StripeEnumField(enum=enums.UsageAction, default=enums.UsageAction.increment, help_text='When using increment the specified quantity will be added to the usage at the specified timestamp. The set action will overwrite the usage quantity at that timestamp. If the subscription has billing thresholds, increment is the only allowed value.') class-attribute instance-attribute
djstripe.models.billing.UsageRecord.description = None class-attribute instance-attribute
djstripe.models.billing.UsageRecord.metadata = None class-attribute instance-attribute
djstripe.models.billing.UsageRecord.quantity = models.PositiveIntegerField(help_text='The quantity of the plan to which the customer should be subscribed.') class-attribute instance-attribute
djstripe.models.billing.UsageRecord.stripe_class = stripe.UsageRecord class-attribute instance-attribute
djstripe.models.billing.UsageRecord.subscription_item = StripeForeignKey('SubscriptionItem', on_delete=models.CASCADE, related_name='usage_records', help_text='The subscription item this usage record contains data for.') class-attribute instance-attribute
djstripe.models.billing.UsageRecord.timestamp = StripeDateTimeField(null=True, blank=True, help_text='The timestamp for the usage event. This timestamp must be within the current billing period of the subscription of the provided subscription_item.') class-attribute instance-attribute

Functions

djstripe.models.billing.UsageRecord.__str__()
Source code in djstripe/models/billing.py
2799
2800
def __str__(self):
    return f"Usage for {self.subscription_item} ({self.action}) is {self.quantity}"
djstripe.models.billing.UsageRecord.create(**kwargs) classmethod

A wrapper around _api_create() to allow one to create and sync UsageRecord Objects

Source code in djstripe/models/billing.py
2830
2831
2832
2833
2834
2835
@classmethod
def create(cls, **kwargs):
    """
    A wrapper around _api_create() to allow one to create and sync UsageRecord Objects
    """
    return cls._api_create(**kwargs)

djstripe.models.billing.UsageRecordSummary

Bases: StripeModel

Usage record summaries provides usage information that's been summarized from multiple usage records and over a subscription billing period (e.g., 15 usage records in the month of September). Since new usage records can still be added, the returned summary information for the subscription item's ID should be seen as unstable until the subscription billing period ends.

Stripe documentation: https://stripe.com/docs/api/usage_records/subscription_item_summary_list?lang=python

Source code in djstripe/models/billing.py
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
class UsageRecordSummary(StripeModel):
    """
    Usage record summaries provides usage information that's been summarized
    from multiple usage records and over a subscription billing period
    (e.g., 15 usage records in the month of September).
    Since new usage records can still be added, the returned summary information for the subscription item's ID
    should be seen as unstable until the subscription billing period ends.

    Stripe documentation: https://stripe.com/docs/api/usage_records/subscription_item_summary_list?lang=python
    """

    stripe_class = stripe.UsageRecordSummary

    description = None
    metadata = None

    invoice = StripeForeignKey(
        "Invoice",
        null=True,
        blank=True,
        on_delete=models.CASCADE,
        related_name="usage_record_summaries",
    )
    period = JSONField(
        null=True,
        blank=True,
        help_text="Subscription Billing period for the SubscriptionItem",
    )
    period_end = StripeDateTimeField(
        null=True,
        blank=True,
        help_text="End of the Subscription Billing period for the SubscriptionItem",
    )
    period_start = StripeDateTimeField(
        null=True,
        blank=True,
        help_text="Start of the Subscription Billing period for the SubscriptionItem",
    )
    total_usage = models.PositiveIntegerField(
        help_text="The quantity of the plan to which the customer should be subscribed."
    )
    subscription_item = StripeForeignKey(
        "SubscriptionItem",
        on_delete=models.CASCADE,
        related_name="usage_record_summaries",
        help_text="The subscription item this usage record contains data for.",
    )

    def __str__(self):
        return (
            f"Usage Summary for {self.subscription_item} ({self.invoice}) is"
            f" {self.total_usage}"
        )

    @classmethod
    def _manipulate_stripe_object_hook(cls, data):
        data["period_start"] = data["period"]["start"]
        data["period_end"] = data["period"]["end"]

        return data

    @classmethod
    def api_list(cls, api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs):
        """
        Call the stripe API's list operation for this model.

        :param api_key: The api key to use for this request. \
            Defaults to djstripe_settings.STRIPE_SECRET_KEY.
        :type api_key: string

        See Stripe documentation for accepted kwargs for each object.

        :returns: an iterator over all items in the query
        """
        # Update kwargs with `expand` param
        kwargs = cls.get_expand_params(api_key, **kwargs)

        if not kwargs.get("id"):
            raise KeyError("SubscriptionItem Object ID is missing")

        try:
            SubscriptionItem.objects.get(id=kwargs["id"])
        except SubscriptionItem.DoesNotExist:
            raise

        return stripe.SubscriptionItem.list_usage_record_summaries(
            api_key=api_key,
            stripe_version=djstripe_settings.STRIPE_API_VERSION,
            **kwargs,
        ).auto_paging_iter()

Attributes

djstripe.models.billing.UsageRecordSummary.description = None class-attribute instance-attribute
djstripe.models.billing.UsageRecordSummary.invoice = StripeForeignKey('Invoice', null=True, blank=True, on_delete=models.CASCADE, related_name='usage_record_summaries') class-attribute instance-attribute
djstripe.models.billing.UsageRecordSummary.metadata = None class-attribute instance-attribute
djstripe.models.billing.UsageRecordSummary.period = JSONField(null=True, blank=True, help_text='Subscription Billing period for the SubscriptionItem') class-attribute instance-attribute
djstripe.models.billing.UsageRecordSummary.period_end = StripeDateTimeField(null=True, blank=True, help_text='End of the Subscription Billing period for the SubscriptionItem') class-attribute instance-attribute
djstripe.models.billing.UsageRecordSummary.period_start = StripeDateTimeField(null=True, blank=True, help_text='Start of the Subscription Billing period for the SubscriptionItem') class-attribute instance-attribute
djstripe.models.billing.UsageRecordSummary.stripe_class = stripe.UsageRecordSummary class-attribute instance-attribute
djstripe.models.billing.UsageRecordSummary.subscription_item = StripeForeignKey('SubscriptionItem', on_delete=models.CASCADE, related_name='usage_record_summaries', help_text='The subscription item this usage record contains data for.') class-attribute instance-attribute
djstripe.models.billing.UsageRecordSummary.total_usage = models.PositiveIntegerField(help_text='The quantity of the plan to which the customer should be subscribed.') class-attribute instance-attribute

Functions

djstripe.models.billing.UsageRecordSummary.__str__()
Source code in djstripe/models/billing.py
2886
2887
2888
2889
2890
def __str__(self):
    return (
        f"Usage Summary for {self.subscription_item} ({self.invoice}) is"
        f" {self.total_usage}"
    )
djstripe.models.billing.UsageRecordSummary.api_list(api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs) classmethod

Call the stripe API's list operation for this model.

:param api_key: The api key to use for this request. Defaults to djstripe_settings.STRIPE_SECRET_KEY. :type api_key: string

See Stripe documentation for accepted kwargs for each object.

:returns: an iterator over all items in the query

Source code in djstripe/models/billing.py
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
@classmethod
def api_list(cls, api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs):
    """
    Call the stripe API's list operation for this model.

    :param api_key: The api key to use for this request. \
        Defaults to djstripe_settings.STRIPE_SECRET_KEY.
    :type api_key: string

    See Stripe documentation for accepted kwargs for each object.

    :returns: an iterator over all items in the query
    """
    # Update kwargs with `expand` param
    kwargs = cls.get_expand_params(api_key, **kwargs)

    if not kwargs.get("id"):
        raise KeyError("SubscriptionItem Object ID is missing")

    try:
        SubscriptionItem.objects.get(id=kwargs["id"])
    except SubscriptionItem.DoesNotExist:
        raise

    return stripe.SubscriptionItem.list_usage_record_summaries(
        api_key=api_key,
        stripe_version=djstripe_settings.STRIPE_API_VERSION,
        **kwargs,
    ).auto_paging_iter()

Functions

Connect

Attributes

Classes

djstripe.models.account.Account

Bases: StripeModel

This is an object representing a Stripe account.

You can retrieve it to see properties on the account like its current e-mail address or if the account is enabled yet to make live charges.

Stripe documentation: https://stripe.com/docs/api/accounts?lang=python

Source code in djstripe/models/account.py
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
class Account(StripeModel):
    """
    This is an object representing a Stripe account.

    You can retrieve it to see properties on the account like its
    current e-mail address or if the account is enabled yet to make live charges.

    Stripe documentation: https://stripe.com/docs/api/accounts?lang=python
    """

    stripe_class = stripe.Account
    business_profile = JSONField(
        null=True, blank=True, help_text="Optional information related to the business."
    )
    business_type = StripeEnumField(
        enum=enums.BusinessType, default="", blank=True, help_text="The business type."
    )
    charges_enabled = models.BooleanField(
        help_text="Whether the account can create live charges"
    )
    country = models.CharField(max_length=2, help_text="The country of the account")
    company = JSONField(
        null=True,
        blank=True,
        help_text=(
            "Information about the company or business. "
            "This field is null unless business_type is set to company."
        ),
    )
    default_currency = StripeCurrencyCodeField(
        help_text="The currency this account has chosen to use as the default"
    )
    details_submitted = models.BooleanField(
        help_text=(
            "Whether account details have been submitted. "
            "Standard accounts cannot receive payouts before this is true."
        )
    )
    email = models.CharField(
        max_length=255, help_text="The primary user's email address."
    )
    # TODO external_accounts = ...
    individual = JSONField(
        null=True,
        blank=True,
        help_text=(
            "Information about the person represented by the account. "
            "This field is null unless business_type is set to individual."
        ),
    )
    payouts_enabled = models.BooleanField(
        null=True, help_text="Whether Stripe can send payouts to this account"
    )
    product_description = models.CharField(
        max_length=255,
        default="",
        blank=True,
        help_text=(
            "Internal-only description of the product sold or service provided "
            "by the business. It's used by Stripe for risk and underwriting purposes."
        ),
    )
    requirements = JSONField(
        null=True,
        blank=True,
        help_text=(
            "Information about the requirements for the account, "
            "including what information needs to be collected, and by when."
        ),
    )
    settings = JSONField(
        null=True,
        blank=True,
        help_text=(
            "Account options for customizing how the account functions within Stripe."
        ),
    )
    type = StripeEnumField(enum=enums.AccountType, help_text="The Stripe account type.")
    tos_acceptance = JSONField(
        null=True,
        blank=True,
        help_text="Details on the acceptance of the Stripe Services Agreement",
    )

    def get_stripe_dashboard_url(self) -> str:
        """Get the stripe dashboard url for this object."""
        return (
            f"https://dashboard.stripe.com/{self.id}/"
            f"{'test/' if not self.livemode else ''}dashboard"
        )

    @property
    def default_api_key(self) -> str:
        return self.get_default_api_key()

    def get_default_api_key(self, livemode: bool = None) -> str:
        if livemode is None:
            livemode = self.livemode
            api_key = APIKey.objects.filter(
                djstripe_owner_account=self, type=APIKeyType.secret
            ).first()
        else:
            api_key = APIKey.objects.filter(
                djstripe_owner_account=self, type=APIKeyType.secret, livemode=livemode
            ).first()

        if api_key:
            return api_key.secret
        return djstripe_settings.get_default_api_key(livemode)

    @property
    def business_url(self) -> str:
        """
        The business's publicly available website.
        """
        if self.business_profile:
            return self.business_profile.get("url", "")
        return ""

    @classmethod
    def get_default_account(cls, api_key=djstripe_settings.STRIPE_SECRET_KEY):
        # As of API version 2020-03-02, there is no permission that can allow
        # restricted keys to call GET /v1/account
        if djstripe_settings.STRIPE_SECRET_KEY.startswith("rk_"):
            return None

        account_data = cls.stripe_class.retrieve(
            api_key=api_key, stripe_version=djstripe_settings.STRIPE_API_VERSION
        )

        return cls._get_or_create_from_stripe_object(account_data, api_key=api_key)[0]

    @classmethod
    def get_or_retrieve_for_api_key(cls, api_key: str):
        with transaction.atomic():
            apikey_instance, _ = APIKey.objects.get_or_create_by_api_key(api_key)
            if not apikey_instance.djstripe_owner_account:
                apikey_instance.refresh_account()

            return apikey_instance.djstripe_owner_account

    def __str__(self):
        settings = self.settings or {}
        business_profile = self.business_profile or {}
        return (
            settings.get("dashboard", {}).get("display_name")
            or business_profile.get("name")
            or super().__str__()
        )

    def api_reject(self, api_key=None, stripe_account=None, **kwargs):
        """
        Call the stripe API's reject operation for Account model

        :param api_key: The api key to use for this request.
            Defaults to djstripe_settings.STRIPE_SECRET_KEY.
        :type api_key: string
        :param stripe_account: The optional connected account \
            for which this request is being made.
        :type stripe_account: string
        """
        api_key = api_key or self.default_api_key

        # Prefer passed in stripe_account if set.
        if not stripe_account:
            stripe_account = self._get_stripe_account_id(api_key)

        return self.stripe_class.reject(
            self.id,
            api_key=api_key,
            stripe_account=stripe_account,
            stripe_version=djstripe_settings.STRIPE_API_VERSION,
            **kwargs,
        )

    @classmethod
    def _create_from_stripe_object(
        cls,
        data,
        current_ids=None,
        pending_relations=None,
        save=True,
        stripe_account=None,
        api_key=djstripe_settings.STRIPE_SECRET_KEY,
    ):
        """
        Set the stripe_account to the id of the Account instance being created.

        This ensures that the foreign-key relations that may exist in stripe are
        fetched using the appropriate connected account ID.
        """
        return super()._create_from_stripe_object(
            data=data,
            current_ids=current_ids,
            pending_relations=pending_relations,
            save=save,
            stripe_account=data["id"] if not stripe_account else stripe_account,
            api_key=api_key,
        )

    # "Special" handling of the icon and logo fields
    # Previously available as properties, they moved to
    # settings.branding in Stripe 2019-02-19.
    # Currently, they return a File ID
    @property
    def branding_icon(self):
        from ..models.core import File

        id = self.settings.get("branding", {}).get("icon")
        return File.objects.filter(id=id).first() if id else None

    @property
    def branding_logo(self):
        from ..models.core import File

        id = self.settings.get("branding", {}).get("logo")
        return File.objects.filter(id=id).first() if id else None

    def _attach_objects_post_save_hook(
        self,
        cls,
        data,
        pending_relations=None,
        api_key=djstripe_settings.STRIPE_SECRET_KEY,
    ):
        from ..models.core import File

        super()._attach_objects_post_save_hook(
            cls, data, pending_relations=pending_relations, api_key=api_key
        )

        # set the livemode if not returned by data
        if "livemode" not in data.keys() and self.djstripe_owner_account is not None:
            # Platform Account
            if self == self.djstripe_owner_account:
                self.livemode = None
            else:
                # Connected Account
                _, self.livemode = get_api_key_details_by_prefix(api_key)

        # save the updates
        self.save()

        # Retrieve and save the Files in the settings.branding object.
        for field in "icon", "logo":
            file_upload_id = self.settings and self.settings.get("branding", {}).get(
                field
            )
            if file_upload_id:
                try:
                    File.sync_from_stripe_data(
                        File(id=file_upload_id).api_retrieve(
                            stripe_account=self.id, api_key=api_key
                        ),
                        api_key=api_key,
                    )
                except stripe.error.PermissionError:
                    # No permission to retrieve the data with the key
                    logger.warning(
                        f"Cannot retrieve business branding {field} for acct"
                        f" {self.id} with the key."
                    )
                except stripe.error.InvalidRequestError as e:
                    if "a similar object exists in" in str(e):
                        # HACK around a Stripe bug.
                        # See #830 and commit c09d25f52bfdcf883e9eec0bf6c25af1771a644a
                        pass
                    else:
                        raise
                except stripe.error.AuthenticationError:
                    # This may happen if saving an account that has a logo, using
                    # a different API key to the default.
                    # OK, concretely, there is a chicken-and-egg problem here.
                    # But, the logo file object is not a particularly important thing.
                    # Until we have a better solution, just ignore this error.
                    pass

Attributes

djstripe.models.account.Account.branding_icon property
djstripe.models.account.Account.business_profile = JSONField(null=True, blank=True, help_text='Optional information related to the business.') class-attribute instance-attribute
djstripe.models.account.Account.business_type = StripeEnumField(enum=enums.BusinessType, default='', blank=True, help_text='The business type.') class-attribute instance-attribute
djstripe.models.account.Account.business_url: str property

The business's publicly available website.

djstripe.models.account.Account.charges_enabled = models.BooleanField(help_text='Whether the account can create live charges') class-attribute instance-attribute
djstripe.models.account.Account.company = JSONField(null=True, blank=True, help_text='Information about the company or business. This field is null unless business_type is set to company.') class-attribute instance-attribute
djstripe.models.account.Account.country = models.CharField(max_length=2, help_text='The country of the account') class-attribute instance-attribute
djstripe.models.account.Account.default_api_key: str property
djstripe.models.account.Account.default_currency = StripeCurrencyCodeField(help_text='The currency this account has chosen to use as the default') class-attribute instance-attribute
djstripe.models.account.Account.details_submitted = models.BooleanField(help_text='Whether account details have been submitted. Standard accounts cannot receive payouts before this is true.') class-attribute instance-attribute
djstripe.models.account.Account.email = models.CharField(max_length=255, help_text="The primary user's email address.") class-attribute instance-attribute
djstripe.models.account.Account.individual = JSONField(null=True, blank=True, help_text='Information about the person represented by the account. This field is null unless business_type is set to individual.') class-attribute instance-attribute
djstripe.models.account.Account.payouts_enabled = models.BooleanField(null=True, help_text='Whether Stripe can send payouts to this account') class-attribute instance-attribute
djstripe.models.account.Account.product_description = models.CharField(max_length=255, default='', blank=True, help_text="Internal-only description of the product sold or service provided by the business. It's used by Stripe for risk and underwriting purposes.") class-attribute instance-attribute
djstripe.models.account.Account.requirements = JSONField(null=True, blank=True, help_text='Information about the requirements for the account, including what information needs to be collected, and by when.') class-attribute instance-attribute
djstripe.models.account.Account.settings = JSONField(null=True, blank=True, help_text='Account options for customizing how the account functions within Stripe.') class-attribute instance-attribute
djstripe.models.account.Account.stripe_class = stripe.Account class-attribute instance-attribute
djstripe.models.account.Account.tos_acceptance = JSONField(null=True, blank=True, help_text='Details on the acceptance of the Stripe Services Agreement') class-attribute instance-attribute
djstripe.models.account.Account.type = StripeEnumField(enum=enums.AccountType, help_text='The Stripe account type.') class-attribute instance-attribute

Functions

djstripe.models.account.Account.__str__()
Source code in djstripe/models/account.py
153
154
155
156
157
158
159
160
def __str__(self):
    settings = self.settings or {}
    business_profile = self.business_profile or {}
    return (
        settings.get("dashboard", {}).get("display_name")
        or business_profile.get("name")
        or super().__str__()
    )
djstripe.models.account.Account.api_reject(api_key=None, stripe_account=None, **kwargs)

Call the stripe API's reject operation for Account model

:param api_key: The api key to use for this request. Defaults to djstripe_settings.STRIPE_SECRET_KEY. :type api_key: string :param stripe_account: The optional connected account for which this request is being made. :type stripe_account: string

Source code in djstripe/models/account.py
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
def api_reject(self, api_key=None, stripe_account=None, **kwargs):
    """
    Call the stripe API's reject operation for Account model

    :param api_key: The api key to use for this request.
        Defaults to djstripe_settings.STRIPE_SECRET_KEY.
    :type api_key: string
    :param stripe_account: The optional connected account \
        for which this request is being made.
    :type stripe_account: string
    """
    api_key = api_key or self.default_api_key

    # Prefer passed in stripe_account if set.
    if not stripe_account:
        stripe_account = self._get_stripe_account_id(api_key)

    return self.stripe_class.reject(
        self.id,
        api_key=api_key,
        stripe_account=stripe_account,
        stripe_version=djstripe_settings.STRIPE_API_VERSION,
        **kwargs,
    )
djstripe.models.account.Account.get_default_account(api_key=djstripe_settings.STRIPE_SECRET_KEY) classmethod
Source code in djstripe/models/account.py
131
132
133
134
135
136
137
138
139
140
141
142
@classmethod
def get_default_account(cls, api_key=djstripe_settings.STRIPE_SECRET_KEY):
    # As of API version 2020-03-02, there is no permission that can allow
    # restricted keys to call GET /v1/account
    if djstripe_settings.STRIPE_SECRET_KEY.startswith("rk_"):
        return None

    account_data = cls.stripe_class.retrieve(
        api_key=api_key, stripe_version=djstripe_settings.STRIPE_API_VERSION
    )

    return cls._get_or_create_from_stripe_object(account_data, api_key=api_key)[0]
djstripe.models.account.Account.get_default_api_key(livemode=None)
Source code in djstripe/models/account.py
107
108
109
110
111
112
113
114
115
116
117
118
119
120
def get_default_api_key(self, livemode: bool = None) -> str:
    if livemode is None:
        livemode = self.livemode
        api_key = APIKey.objects.filter(
            djstripe_owner_account=self, type=APIKeyType.secret
        ).first()
    else:
        api_key = APIKey.objects.filter(
            djstripe_owner_account=self, type=APIKeyType.secret, livemode=livemode
        ).first()

    if api_key:
        return api_key.secret
    return djstripe_settings.get_default_api_key(livemode)
djstripe.models.account.Account.get_or_retrieve_for_api_key(api_key) classmethod
Source code in djstripe/models/account.py
144
145
146
147
148
149
150
151
@classmethod
def get_or_retrieve_for_api_key(cls, api_key: str):
    with transaction.atomic():
        apikey_instance, _ = APIKey.objects.get_or_create_by_api_key(api_key)
        if not apikey_instance.djstripe_owner_account:
            apikey_instance.refresh_account()

        return apikey_instance.djstripe_owner_account
djstripe.models.account.Account.get_stripe_dashboard_url()

Get the stripe dashboard url for this object.

Source code in djstripe/models/account.py
 96
 97
 98
 99
100
101
def get_stripe_dashboard_url(self) -> str:
    """Get the stripe dashboard url for this object."""
    return (
        f"https://dashboard.stripe.com/{self.id}/"
        f"{'test/' if not self.livemode else ''}dashboard"
    )

Functions

Attributes

Classes

djstripe.models.connect.ApplicationFee

Bases: StripeModel

When you collect a transaction fee on top of a charge made for your user (using Connect), an ApplicationFee is created in your account.

Please note the model field charge exists on the Stripe Connected Account while the application_fee modelfield on Charge model exists on the Platform Account!

Stripe documentation: https://stripe.com/docs/api?lang=python#application_fees

Source code in djstripe/models/connect.py
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
class ApplicationFee(StripeModel):
    """
    When you collect a transaction fee on top of a charge made for your
    user (using Connect), an ApplicationFee is created in your account.

    Please note the model field charge exists on the Stripe Connected Account
    while the application_fee modelfield on Charge model exists on the Platform Account!

    Stripe documentation: https://stripe.com/docs/api?lang=python#application_fees
    """

    stripe_class = stripe.ApplicationFee
    account = StripeForeignKey(
        "Account",
        on_delete=models.PROTECT,
        related_name="application_fees",
        help_text="ID of the Stripe account this fee was taken from.",
    )
    amount = StripeQuantumCurrencyAmountField(help_text="Amount earned, in cents.")
    amount_refunded = StripeQuantumCurrencyAmountField(
        help_text=(
            "Amount in cents refunded (can be less than the amount attribute "
            "on the fee if a partial refund was issued)"
        )
    )
    # TODO application = ...
    # balance_transaction exists on the platform account
    balance_transaction = StripeForeignKey(
        "BalanceTransaction",
        on_delete=models.CASCADE,
        help_text=(
            "Balance transaction that describes the impact on your account balance."
        ),
    )
    # charge exists on the Stripe Connected Account and not the Platform Account
    charge = StripeForeignKey(
        "Charge",
        on_delete=models.CASCADE,
        help_text="The charge that the application fee was taken from.",
    )
    currency = StripeCurrencyCodeField()
    # TODO originating_transaction = ... (refs. both Charge and Transfer)
    refunded = models.BooleanField(
        help_text=(
            "Whether the fee has been fully refunded. If the fee is only "
            "partially refunded, this attribute will still be false."
        )
    )

Attributes

djstripe.models.connect.ApplicationFee.account = StripeForeignKey('Account', on_delete=models.PROTECT, related_name='application_fees', help_text='ID of the Stripe account this fee was taken from.') class-attribute instance-attribute
djstripe.models.connect.ApplicationFee.amount = StripeQuantumCurrencyAmountField(help_text='Amount earned, in cents.') class-attribute instance-attribute
djstripe.models.connect.ApplicationFee.amount_refunded = StripeQuantumCurrencyAmountField(help_text='Amount in cents refunded (can be less than the amount attribute on the fee if a partial refund was issued)') class-attribute instance-attribute
djstripe.models.connect.ApplicationFee.balance_transaction = StripeForeignKey('BalanceTransaction', on_delete=models.CASCADE, help_text='Balance transaction that describes the impact on your account balance.') class-attribute instance-attribute
djstripe.models.connect.ApplicationFee.charge = StripeForeignKey('Charge', on_delete=models.CASCADE, help_text='The charge that the application fee was taken from.') class-attribute instance-attribute
djstripe.models.connect.ApplicationFee.currency = StripeCurrencyCodeField() class-attribute instance-attribute
djstripe.models.connect.ApplicationFee.refunded = models.BooleanField(help_text='Whether the fee has been fully refunded. If the fee is only partially refunded, this attribute will still be false.') class-attribute instance-attribute
djstripe.models.connect.ApplicationFee.stripe_class = stripe.ApplicationFee class-attribute instance-attribute

djstripe.models.connect.ApplicationFeeRefund

Bases: StripeModel

ApplicationFeeRefund objects allow you to refund an ApplicationFee that has previously been created but not yet refunded. Funds will be refunded to the Stripe account from which the fee was originally collected.

Stripe documentation: https://stripe.com/docs/api?lang=python#fee_refunds

Source code in djstripe/models/connect.py
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
class ApplicationFeeRefund(StripeModel):
    """
    ApplicationFeeRefund objects allow you to refund an ApplicationFee that
    has previously been created but not yet refunded.
    Funds will be refunded to the Stripe account from which the fee was
    originally collected.

    Stripe documentation: https://stripe.com/docs/api?lang=python#fee_refunds
    """

    description = None
    stripe_class = stripe.ApplicationFeeRefund

    amount = StripeQuantumCurrencyAmountField(help_text="Amount refunded, in cents.")
    balance_transaction = StripeForeignKey(
        "BalanceTransaction",
        on_delete=models.CASCADE,
        help_text=(
            "Balance transaction that describes the impact on your account balance."
        ),
    )
    currency = StripeCurrencyCodeField()
    fee = StripeForeignKey(
        "ApplicationFee",
        on_delete=models.CASCADE,
        related_name="refunds",
        help_text="The application fee that was refunded",
    )

Attributes

djstripe.models.connect.ApplicationFeeRefund.amount = StripeQuantumCurrencyAmountField(help_text='Amount refunded, in cents.') class-attribute instance-attribute
djstripe.models.connect.ApplicationFeeRefund.balance_transaction = StripeForeignKey('BalanceTransaction', on_delete=models.CASCADE, help_text='Balance transaction that describes the impact on your account balance.') class-attribute instance-attribute
djstripe.models.connect.ApplicationFeeRefund.currency = StripeCurrencyCodeField() class-attribute instance-attribute
djstripe.models.connect.ApplicationFeeRefund.description = None class-attribute instance-attribute
djstripe.models.connect.ApplicationFeeRefund.fee = StripeForeignKey('ApplicationFee', on_delete=models.CASCADE, related_name='refunds', help_text='The application fee that was refunded') class-attribute instance-attribute
djstripe.models.connect.ApplicationFeeRefund.stripe_class = stripe.ApplicationFeeRefund class-attribute instance-attribute

djstripe.models.connect.CountrySpec

Bases: StripeBaseModel

Stripe documentation: https://stripe.com/docs/api?lang=python#country_specs

Source code in djstripe/models/connect.py
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
class CountrySpec(StripeBaseModel):
    """
    Stripe documentation: https://stripe.com/docs/api?lang=python#country_specs
    """

    stripe_class = stripe.CountrySpec

    id = models.CharField(max_length=2, primary_key=True, serialize=True)

    default_currency = StripeCurrencyCodeField(
        help_text=(
            "The default currency for this country. "
            "This applies to both payment methods and bank accounts."
        )
    )
    supported_bank_account_currencies = JSONField(
        help_text=(
            "Currencies that can be accepted in the specific country (for transfers)."
        )
    )
    supported_payment_currencies = JSONField(
        help_text=(
            "Currencies that can be accepted in the specified country (for payments)."
        )
    )
    supported_payment_methods = JSONField(
        help_text="Payment methods available in the specified country."
    )
    supported_transfer_countries = JSONField(
        help_text="Countries that can accept transfers from the specified country."
    )
    verification_fields = JSONField(
        help_text="Lists the types of verification data needed to keep an account open."
    )

    @classmethod
    def sync_from_stripe_data(
        cls, data, api_key=djstripe_settings.STRIPE_SECRET_KEY
    ) -> "CountrySpec":
        """
        Syncs this object from the stripe data provided.

        Foreign keys will also be retrieved and synced recursively.

        :param data: stripe object
        :type data: dict
        :rtype: cls
        """
        data_id = data["id"]

        supported_fields = (
            "default_currency",
            "supported_bank_account_currencies",
            "supported_payment_currencies",
            "supported_payment_methods",
            "supported_transfer_countries",
            "verification_fields",
        )

        instance, created = cls.objects.get_or_create(
            id=data_id,
            defaults={k: data[k] for k in supported_fields},
        )

        return instance

    def api_retrieve(self, api_key: str = None, stripe_account=None):
        if api_key is None:
            api_key = djstripe_settings.get_default_api_key(livemode=None)

        return self.stripe_class.retrieve(
            id=self.id,
            api_key=api_key,
            stripe_version=djstripe_settings.STRIPE_API_VERSION,
            stripe_account=stripe_account,
        )

Attributes

djstripe.models.connect.CountrySpec.default_currency = StripeCurrencyCodeField(help_text='The default currency for this country. This applies to both payment methods and bank accounts.') class-attribute instance-attribute
djstripe.models.connect.CountrySpec.id = models.CharField(max_length=2, primary_key=True, serialize=True) class-attribute instance-attribute
djstripe.models.connect.CountrySpec.stripe_class = stripe.CountrySpec class-attribute instance-attribute
djstripe.models.connect.CountrySpec.supported_bank_account_currencies = JSONField(help_text='Currencies that can be accepted in the specific country (for transfers).') class-attribute instance-attribute
djstripe.models.connect.CountrySpec.supported_payment_currencies = JSONField(help_text='Currencies that can be accepted in the specified country (for payments).') class-attribute instance-attribute
djstripe.models.connect.CountrySpec.supported_payment_methods = JSONField(help_text='Payment methods available in the specified country.') class-attribute instance-attribute
djstripe.models.connect.CountrySpec.supported_transfer_countries = JSONField(help_text='Countries that can accept transfers from the specified country.') class-attribute instance-attribute
djstripe.models.connect.CountrySpec.verification_fields = JSONField(help_text='Lists the types of verification data needed to keep an account open.') class-attribute instance-attribute

Functions

djstripe.models.connect.CountrySpec.api_retrieve(api_key=None, stripe_account=None)
Source code in djstripe/models/connect.py
169
170
171
172
173
174
175
176
177
178
def api_retrieve(self, api_key: str = None, stripe_account=None):
    if api_key is None:
        api_key = djstripe_settings.get_default_api_key(livemode=None)

    return self.stripe_class.retrieve(
        id=self.id,
        api_key=api_key,
        stripe_version=djstripe_settings.STRIPE_API_VERSION,
        stripe_account=stripe_account,
    )
djstripe.models.connect.CountrySpec.sync_from_stripe_data(data, api_key=djstripe_settings.STRIPE_SECRET_KEY) classmethod

Syncs this object from the stripe data provided.

Foreign keys will also be retrieved and synced recursively.

:param data: stripe object :type data: dict :rtype: cls

Source code in djstripe/models/connect.py
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
@classmethod
def sync_from_stripe_data(
    cls, data, api_key=djstripe_settings.STRIPE_SECRET_KEY
) -> "CountrySpec":
    """
    Syncs this object from the stripe data provided.

    Foreign keys will also be retrieved and synced recursively.

    :param data: stripe object
    :type data: dict
    :rtype: cls
    """
    data_id = data["id"]

    supported_fields = (
        "default_currency",
        "supported_bank_account_currencies",
        "supported_payment_currencies",
        "supported_payment_methods",
        "supported_transfer_countries",
        "verification_fields",
    )

    instance, created = cls.objects.get_or_create(
        id=data_id,
        defaults={k: data[k] for k in supported_fields},
    )

    return instance

djstripe.models.connect.Transfer

Bases: StripeModel

When Stripe sends you money or you initiate a transfer to a bank account, debit card, or connected Stripe account, a transfer object will be created.

Stripe documentation: https://stripe.com/docs/api?lang=python#transfers

Source code in djstripe/models/connect.py
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
class Transfer(StripeModel):
    """
    When Stripe sends you money or you initiate a transfer to a bank account,
    debit card, or connected Stripe account, a transfer object will be created.

    Stripe documentation: https://stripe.com/docs/api?lang=python#transfers
    """

    stripe_class = stripe.Transfer
    expand_fields = ["balance_transaction"]
    stripe_dashboard_item_name = "transfers"

    objects = TransferManager()

    amount = StripeDecimalCurrencyAmountField(help_text="The amount transferred")
    amount_reversed = StripeDecimalCurrencyAmountField(
        null=True,
        blank=True,
        help_text=(
            "The amount (as decimal) reversed (can be less than the amount "
            "attribute on the transfer if a partial reversal was issued)."
        ),
    )
    balance_transaction = StripeForeignKey(
        "BalanceTransaction",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        help_text=(
            "Balance transaction that describes the impact on your account balance."
        ),
    )
    currency = StripeCurrencyCodeField()

    destination = StripeIdField(
        max_length=255,
        null=True,
        help_text=(
            "ID of the bank account, card, or Stripe account the transfer was sent to."
        ),
    )

    # todo implement payment model (for some reason py ids are showing up in the charge model)
    destination_payment = StripeIdField(
        null=True,
        blank=True,
        help_text=(
            "If the destination is a Stripe account, this will be the ID of the "
            "payment that the destination account received for the transfer."
        ),
    )
    reversed = models.BooleanField(
        default=False,
        help_text=(
            "Whether or not the transfer has been fully reversed. "
            "If the transfer is only partially reversed, this attribute will still "
            "be false."
        ),
    )
    source_transaction = StripeIdField(
        null=True,
        help_text=(
            "ID of the charge (or other transaction) that was used to fund "
            "the transfer. If null, the transfer was funded from the available balance."
        ),
    )
    source_type = StripeEnumField(
        enum=enums.LegacySourceType,
        help_text="The source balance from which this transfer came.",
    )
    transfer_group = models.CharField(
        max_length=255,
        default="",
        blank=True,
        help_text="A string that identifies this transaction as part of a group.",
    )

    @property
    def fee(self):
        if self.balance_transaction:
            return self.balance_transaction.fee

    def __str__(self):
        amount = get_friendly_currency_amount(self.amount, self.currency)
        if self.reversed:
            # Complete Reversal
            return f"{amount} Reversed"
        elif self.amount_reversed:
            # Partial Reversal
            return f"{amount} Partially Reversed"
        # No Reversal
        return f"{amount}"

    def _attach_objects_post_save_hook(
        self,
        cls,
        data,
        api_key=djstripe_settings.STRIPE_SECRET_KEY,
        pending_relations=None,
    ):
        """
        Iterate over reversals on the Transfer object to create and/or sync
        TransferReversal objects
        """

        super()._attach_objects_post_save_hook(
            cls, data, api_key=api_key, pending_relations=pending_relations
        )

        # Transfer Reversals exist as a list on the Transfer Object
        for reversals_data in data.get("reversals").auto_paging_iter():
            TransferReversal.sync_from_stripe_data(reversals_data, api_key=api_key)

    def get_stripe_dashboard_url(self) -> str:
        return (
            f"{self._get_base_stripe_dashboard_url()}"
            f"connect/{self.stripe_dashboard_item_name}/{self.id}"
        )

Attributes

djstripe.models.connect.Transfer.amount = StripeDecimalCurrencyAmountField(help_text='The amount transferred') class-attribute instance-attribute
djstripe.models.connect.Transfer.amount_reversed = StripeDecimalCurrencyAmountField(null=True, blank=True, help_text='The amount (as decimal) reversed (can be less than the amount attribute on the transfer if a partial reversal was issued).') class-attribute instance-attribute
djstripe.models.connect.Transfer.balance_transaction = StripeForeignKey('BalanceTransaction', on_delete=models.SET_NULL, null=True, blank=True, help_text='Balance transaction that describes the impact on your account balance.') class-attribute instance-attribute
djstripe.models.connect.Transfer.currency = StripeCurrencyCodeField() class-attribute instance-attribute
djstripe.models.connect.Transfer.destination = StripeIdField(max_length=255, null=True, help_text='ID of the bank account, card, or Stripe account the transfer was sent to.') class-attribute instance-attribute
djstripe.models.connect.Transfer.destination_payment = StripeIdField(null=True, blank=True, help_text='If the destination is a Stripe account, this will be the ID of the payment that the destination account received for the transfer.') class-attribute instance-attribute
djstripe.models.connect.Transfer.expand_fields = ['balance_transaction'] class-attribute instance-attribute
djstripe.models.connect.Transfer.fee property
djstripe.models.connect.Transfer.objects = TransferManager() class-attribute instance-attribute
djstripe.models.connect.Transfer.reversed = models.BooleanField(default=False, help_text='Whether or not the transfer has been fully reversed. If the transfer is only partially reversed, this attribute will still be false.') class-attribute instance-attribute
djstripe.models.connect.Transfer.source_transaction = StripeIdField(null=True, help_text='ID of the charge (or other transaction) that was used to fund the transfer. If null, the transfer was funded from the available balance.') class-attribute instance-attribute
djstripe.models.connect.Transfer.source_type = StripeEnumField(enum=enums.LegacySourceType, help_text='The source balance from which this transfer came.') class-attribute instance-attribute
djstripe.models.connect.Transfer.stripe_class = stripe.Transfer class-attribute instance-attribute
djstripe.models.connect.Transfer.stripe_dashboard_item_name = 'transfers' class-attribute instance-attribute
djstripe.models.connect.Transfer.transfer_group = models.CharField(max_length=255, default='', blank=True, help_text='A string that identifies this transaction as part of a group.') class-attribute instance-attribute

Functions

djstripe.models.connect.Transfer.__str__()
Source code in djstripe/models/connect.py
263
264
265
266
267
268
269
270
271
272
def __str__(self):
    amount = get_friendly_currency_amount(self.amount, self.currency)
    if self.reversed:
        # Complete Reversal
        return f"{amount} Reversed"
    elif self.amount_reversed:
        # Partial Reversal
        return f"{amount} Partially Reversed"
    # No Reversal
    return f"{amount}"
djstripe.models.connect.Transfer.get_stripe_dashboard_url()
Source code in djstripe/models/connect.py
294
295
296
297
298
def get_stripe_dashboard_url(self) -> str:
    return (
        f"{self._get_base_stripe_dashboard_url()}"
        f"connect/{self.stripe_dashboard_item_name}/{self.id}"
    )

djstripe.models.connect.TransferReversal

Bases: StripeModel

Stripe documentation: https://stripe.com/docs/api?lang=python#transfer_reversals

Source code in djstripe/models/connect.py
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
class TransferReversal(StripeModel):
    """
    Stripe documentation: https://stripe.com/docs/api?lang=python#transfer_reversals
    """

    expand_fields = ["balance_transaction", "transfer"]
    stripe_dashboard_item_name = "transfer_reversals"

    # TransferReversal classmethods are derived from
    # and attached to the stripe.Transfer class
    stripe_class = stripe.Transfer

    amount = StripeQuantumCurrencyAmountField(help_text="Amount, in cents.")
    balance_transaction = StripeForeignKey(
        "BalanceTransaction",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="transfer_reversals",
        help_text=(
            "Balance transaction that describes the impact on your account balance."
        ),
    )
    currency = StripeCurrencyCodeField()
    transfer = StripeForeignKey(
        "Transfer",
        on_delete=models.CASCADE,
        help_text="The transfer that was reversed.",
        related_name="reversals",
    )

    def __str__(self):
        return str(self.transfer)

    @classmethod
    def _api_create(cls, api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs):
        """
        Call the stripe API's create operation for this model.
        :param api_key: The api key to use for this request. \
            Defaults to djstripe_settings.STRIPE_SECRET_KEY.
        :type api_key: string
        """

        if not kwargs.get("id"):
            raise KeyError("Transfer Object ID is missing")

        try:
            Transfer.objects.get(id=kwargs["id"])
        except Transfer.DoesNotExist:
            raise

        return stripe.Transfer.create_reversal(
            api_key=api_key,
            stripe_version=djstripe_settings.STRIPE_API_VERSION,
            **kwargs,
        )

    def api_retrieve(self, api_key=None, stripe_account=None):
        """
        Call the stripe API's retrieve operation for this model.
        :param api_key: The api key to use for this request. \
            Defaults to djstripe_settings.STRIPE_SECRET_KEY.
        :type api_key: string
        :param stripe_account: The optional connected account \
            for which this request is being made.
        :type stripe_account: string
        """
        nested_id = self.id
        id = self.transfer.id
        api_key = api_key or self.default_api_key

        # Prefer passed in stripe_account if set.
        if not stripe_account:
            stripe_account = self._get_stripe_account_id(api_key)

        return stripe.Transfer.retrieve_reversal(
            id=id,
            nested_id=nested_id,
            api_key=api_key,
            stripe_version=djstripe_settings.STRIPE_API_VERSION,
            expand=self.expand_fields,
            stripe_account=stripe_account,
        )

    @classmethod
    def api_list(cls, api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs):
        """
        Call the stripe API's list operation for this model.
        :param api_key: The api key to use for this request. \
            Defaults to djstripe_settings.STRIPE_SECRET_KEY.
        :type api_key: string
        See Stripe documentation for accepted kwargs for each object.
        :returns: an iterator over all items in the query
        """
        # Update kwargs with `expand` param
        kwargs = cls.get_expand_params(api_key, **kwargs)

        return stripe.Transfer.list_reversals(
            api_key=api_key,
            stripe_version=djstripe_settings.STRIPE_API_VERSION,
            **kwargs,
        ).auto_paging_iter()

    @classmethod
    def is_valid_object(cls, data):
        """
        Returns whether the data is a valid object for the class
        """
        return data and data.get("object") == "transfer_reversal"

Attributes

djstripe.models.connect.TransferReversal.amount = StripeQuantumCurrencyAmountField(help_text='Amount, in cents.') class-attribute instance-attribute
djstripe.models.connect.TransferReversal.balance_transaction = StripeForeignKey('BalanceTransaction', on_delete=models.SET_NULL, null=True, blank=True, related_name='transfer_reversals', help_text='Balance transaction that describes the impact on your account balance.') class-attribute instance-attribute
djstripe.models.connect.TransferReversal.currency = StripeCurrencyCodeField() class-attribute instance-attribute
djstripe.models.connect.TransferReversal.expand_fields = ['balance_transaction', 'transfer'] class-attribute instance-attribute
djstripe.models.connect.TransferReversal.stripe_class = stripe.Transfer class-attribute instance-attribute
djstripe.models.connect.TransferReversal.stripe_dashboard_item_name = 'transfer_reversals' class-attribute instance-attribute
djstripe.models.connect.TransferReversal.transfer = StripeForeignKey('Transfer', on_delete=models.CASCADE, help_text='The transfer that was reversed.', related_name='reversals') class-attribute instance-attribute

Functions

djstripe.models.connect.TransferReversal.__str__()
Source code in djstripe/models/connect.py
333
334
def __str__(self):
    return str(self.transfer)
djstripe.models.connect.TransferReversal.api_list(api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs) classmethod

Call the stripe API's list operation for this model. :param api_key: The api key to use for this request. Defaults to djstripe_settings.STRIPE_SECRET_KEY. :type api_key: string See Stripe documentation for accepted kwargs for each object. :returns: an iterator over all items in the query

Source code in djstripe/models/connect.py
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
@classmethod
def api_list(cls, api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs):
    """
    Call the stripe API's list operation for this model.
    :param api_key: The api key to use for this request. \
        Defaults to djstripe_settings.STRIPE_SECRET_KEY.
    :type api_key: string
    See Stripe documentation for accepted kwargs for each object.
    :returns: an iterator over all items in the query
    """
    # Update kwargs with `expand` param
    kwargs = cls.get_expand_params(api_key, **kwargs)

    return stripe.Transfer.list_reversals(
        api_key=api_key,
        stripe_version=djstripe_settings.STRIPE_API_VERSION,
        **kwargs,
    ).auto_paging_iter()
djstripe.models.connect.TransferReversal.api_retrieve(api_key=None, stripe_account=None)

Call the stripe API's retrieve operation for this model. :param api_key: The api key to use for this request. Defaults to djstripe_settings.STRIPE_SECRET_KEY. :type api_key: string :param stripe_account: The optional connected account for which this request is being made. :type stripe_account: string

Source code in djstripe/models/connect.py
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
def api_retrieve(self, api_key=None, stripe_account=None):
    """
    Call the stripe API's retrieve operation for this model.
    :param api_key: The api key to use for this request. \
        Defaults to djstripe_settings.STRIPE_SECRET_KEY.
    :type api_key: string
    :param stripe_account: The optional connected account \
        for which this request is being made.
    :type stripe_account: string
    """
    nested_id = self.id
    id = self.transfer.id
    api_key = api_key or self.default_api_key

    # Prefer passed in stripe_account if set.
    if not stripe_account:
        stripe_account = self._get_stripe_account_id(api_key)

    return stripe.Transfer.retrieve_reversal(
        id=id,
        nested_id=nested_id,
        api_key=api_key,
        stripe_version=djstripe_settings.STRIPE_API_VERSION,
        expand=self.expand_fields,
        stripe_account=stripe_account,
    )
djstripe.models.connect.TransferReversal.is_valid_object(data) classmethod

Returns whether the data is a valid object for the class

Source code in djstripe/models/connect.py
405
406
407
408
409
410
@classmethod
def is_valid_object(cls, data):
    """
    Returns whether the data is a valid object for the class
    """
    return data and data.get("object") == "transfer_reversal"

Functions

Fraud

Orders

Attributes

Classes

djstripe.models.orders.Order

Bases: StripeModel

An Order describes a purchase being made by a customer, including the products & quantities being purchased, the order status, the payment information, and the billing/shipping details.

Stripe documentation: https://stripe.com/docs/api/orders_v2/object?lang=python

Source code in djstripe/models/orders.py
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
class Order(StripeModel):
    """
    An Order describes a purchase being made by a customer,
    including the products & quantities being purchased, the order status,
    the payment information, and the billing/shipping details.

    Stripe documentation: https://stripe.com/docs/api/orders_v2/object?lang=python
    """

    stripe_class = stripe.Order
    expand_fields = ["customer", "line_items", "discounts", "total_details.breakdown"]
    stripe_dashboard_item_name = "orders"

    amount_subtotal = StripeQuantumCurrencyAmountField(
        help_text=(
            "Order cost before any discounts or taxes are applied. A positive integer"
            " representing the subtotal of the order in the smallest currency unit"
            " (e.g., 100 cents to charge $1.00 or 100 to charge ¥100, a zero-decimal"
            " currency)."
        )
    )
    amount_total = StripeQuantumCurrencyAmountField(
        help_text=(
            "Total order cost after discounts and taxes are applied. A positive integer"
            " representing the cost of the order in the smallest currency unit (e.g.,"
            " 100 cents to charge $1.00 or 100 to charge ¥100, a zero-decimal"
            " currency). To submit an order, the total must be either 0 or at least"
            " $0.50 USD or equivalent in charge currency."
        )
    )
    application = models.CharField(
        max_length=255,
        blank=True,
        help_text="ID of the Connect application that created the Order, if any.",
    )
    automatic_tax = JSONField(
        help_text="Settings and latest results for automatic tax lookup for this Order."
    )
    billing_details = JSONField(
        null=True,
        blank=True,
        help_text="Customer billing details associated with the order.",
    )
    client_secret = models.TextField(
        max_length=5000,
        help_text=(
            "The client secret of this PaymentIntent. "
            "Used for client-side retrieval using a publishable key."
        ),
    )
    currency = StripeCurrencyCodeField(
        help_text=(
            "Three-letter ISO currency code, in lowercase. Must be a supported"
            " currency."
        )
    )
    # not deleting order when customer is deleted, because order may be important for taxation and audit purposes
    customer = StripeForeignKey(
        "Customer",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        help_text="The customer which this orders belongs to.",
    )
    discounts = JSONField(
        null=True,
        blank=True,
        help_text="The discounts applied to the order.",
    )
    ip_address = models.GenericIPAddressField(
        null=True,
        blank=True,
        help_text=(
            "A recent IP address of the purchaser used for tax reporting and tax"
            " location inference."
        ),
    )
    line_items = JSONField(
        help_text=(
            "A list of line items the customer is ordering. Each line item includes"
            " information about the product, the quantity, and the resulting cost."
            " There is a maximum of 100 line items."
        ),
    )
    payment = JSONField(
        help_text=(
            "Payment information associated with the order. Includes payment status,"
            " settings, and a PaymentIntent ID"
        ),
    )
    payment_intent = StripeForeignKey(
        "PaymentIntent",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        help_text=(
            "ID of the payment intent associated with this order. Null when the order"
            " is open."
        ),
    )
    shipping_cost = JSONField(
        null=True,
        blank=True,
        help_text=(
            "The details of the customer cost of shipping, including the customer"
            " chosen ShippingRate."
        ),
    )
    shipping_details = JSONField(
        null=True,
        blank=True,
        help_text="Customer shipping information associated with the order.",
    )
    status = StripeEnumField(
        enum=OrderStatus, help_text="The overall status of the order."
    )
    tax_details = JSONField(
        null=True,
        blank=True,
        help_text="Tax details about the purchaser for this order.",
    )
    total_details = JSONField(
        help_text=(
            "Tax, discount, and shipping details for the computed total amount of this"
            " order."
        ),
    )

    def __str__(self):
        template = f"on {self.created.strftime('%m/%d/%Y')} ({self.status})"
        if self.status in (OrderStatus.open, OrderStatus.canceled):
            return "Created " + template
        elif self.status in (
            OrderStatus.submitted,
            OrderStatus.complete,
            OrderStatus.processing,
        ):
            return "Placed " + template
        return self.id

    @classmethod
    def _manipulate_stripe_object_hook(cls, data):
        data["payment_intent"] = data["payment"]["payment_intent"]
        return data

    def _attach_objects_post_save_hook(
        self,
        cls,
        data,
        api_key=djstripe_settings.STRIPE_SECRET_KEY,
        pending_relations=None,
    ):
        super()._attach_objects_post_save_hook(
            cls, data, api_key=api_key, pending_relations=pending_relations
        )

        # sync every discount
        for discount in self.discounts:
            Discount.sync_from_stripe_data(discount, api_key=api_key)

    def cancel(self, api_key=None, stripe_account=None, **kwargs):
        """
        Cancels the order as well as the payment intent if one is attached.

        :param api_key: The api key to use for this request. \
            Defaults to djstripe_settings.STRIPE_SECRET_KEY.
        :type api_key: string
        :param stripe_account: The optional connected account \
            for which this request is being made.
        :type stripe_account: string
        """
        api_key = api_key or self.default_api_key
        # Prefer passed in stripe_account if set.
        if not stripe_account:
            stripe_account = self._get_stripe_account_id(api_key)

        return self.stripe_class.cancel(
            self.id,
            api_key=api_key,
            stripe_account=stripe_account,
            stripe_version=djstripe_settings.STRIPE_API_VERSION,
            **kwargs,
        )

    def reopen(self, api_key=None, stripe_account=None, **kwargs):
        """
        Reopens a submitted order.

        :param api_key: The api key to use for this request. \
            Defaults to djstripe_settings.STRIPE_SECRET_KEY.
        :type api_key: string
        :param stripe_account: The optional connected account \
            for which this request is being made.
        :type stripe_account: string
        """
        api_key = api_key or self.default_api_key
        # Prefer passed in stripe_account if set.
        if not stripe_account:
            stripe_account = self._get_stripe_account_id(api_key)

        return self.stripe_class.reopen(
            self.id,
            api_key=api_key,
            stripe_account=stripe_account,
            stripe_version=djstripe_settings.STRIPE_API_VERSION,
            **kwargs,
        )

    def submit(self, api_key=None, stripe_account=None, **kwargs):
        """
        Submitting an Order transitions the status to processing and creates a PaymentIntent object
        so the order can be paid.
        If the Order has an amount_total of 0, no PaymentIntent object will be created.
        Once the order is submitted, its contents cannot be changed,
        unless the reopen method is called.

        :param api_key: The api key to use for this request. \
            Defaults to djstripe_settings.STRIPE_SECRET_KEY.
        :type api_key: string
        :param stripe_account: The optional connected account \
            for which this request is being made.
        :type stripe_account: string
        """
        api_key = api_key or self.default_api_key
        # Prefer passed in stripe_account if set.
        if not stripe_account:
            stripe_account = self._get_stripe_account_id(api_key)

        return self.stripe_class.submit(
            self.id,
            api_key=api_key,
            stripe_account=stripe_account,
            stripe_version=djstripe_settings.STRIPE_API_VERSION,
            **kwargs,
        )

Attributes

djstripe.models.orders.Order.amount_subtotal = StripeQuantumCurrencyAmountField(help_text='Order cost before any discounts or taxes are applied. A positive integer representing the subtotal of the order in the smallest currency unit (e.g., 100 cents to charge $1.00 or 100 to charge ¥100, a zero-decimal currency).') class-attribute instance-attribute
djstripe.models.orders.Order.amount_total = StripeQuantumCurrencyAmountField(help_text='Total order cost after discounts and taxes are applied. A positive integer representing the cost of the order in the smallest currency unit (e.g., 100 cents to charge $1.00 or 100 to charge ¥100, a zero-decimal currency). To submit an order, the total must be either 0 or at least $0.50 USD or equivalent in charge currency.') class-attribute instance-attribute
djstripe.models.orders.Order.application = models.CharField(max_length=255, blank=True, help_text='ID of the Connect application that created the Order, if any.') class-attribute instance-attribute
djstripe.models.orders.Order.automatic_tax = JSONField(help_text='Settings and latest results for automatic tax lookup for this Order.') class-attribute instance-attribute
djstripe.models.orders.Order.billing_details = JSONField(null=True, blank=True, help_text='Customer billing details associated with the order.') class-attribute instance-attribute
djstripe.models.orders.Order.client_secret = models.TextField(max_length=5000, help_text='The client secret of this PaymentIntent. Used for client-side retrieval using a publishable key.') class-attribute instance-attribute
djstripe.models.orders.Order.currency = StripeCurrencyCodeField(help_text='Three-letter ISO currency code, in lowercase. Must be a supported currency.') class-attribute instance-attribute
djstripe.models.orders.Order.customer = StripeForeignKey('Customer', on_delete=models.SET_NULL, null=True, blank=True, help_text='The customer which this orders belongs to.') class-attribute instance-attribute
djstripe.models.orders.Order.discounts = JSONField(null=True, blank=True, help_text='The discounts applied to the order.') class-attribute instance-attribute
djstripe.models.orders.Order.expand_fields = ['customer', 'line_items', 'discounts', 'total_details.breakdown'] class-attribute instance-attribute
djstripe.models.orders.Order.ip_address = models.GenericIPAddressField(null=True, blank=True, help_text='A recent IP address of the purchaser used for tax reporting and tax location inference.') class-attribute instance-attribute
djstripe.models.orders.Order.line_items = JSONField(help_text='A list of line items the customer is ordering. Each line item includes information about the product, the quantity, and the resulting cost. There is a maximum of 100 line items.') class-attribute instance-attribute
djstripe.models.orders.Order.payment = JSONField(help_text='Payment information associated with the order. Includes payment status, settings, and a PaymentIntent ID') class-attribute instance-attribute
djstripe.models.orders.Order.payment_intent = StripeForeignKey('PaymentIntent', on_delete=models.SET_NULL, null=True, blank=True, help_text='ID of the payment intent associated with this order. Null when the order is open.') class-attribute instance-attribute
djstripe.models.orders.Order.shipping_cost = JSONField(null=True, blank=True, help_text='The details of the customer cost of shipping, including the customer chosen ShippingRate.') class-attribute instance-attribute
djstripe.models.orders.Order.shipping_details = JSONField(null=True, blank=True, help_text='Customer shipping information associated with the order.') class-attribute instance-attribute
djstripe.models.orders.Order.status = StripeEnumField(enum=OrderStatus, help_text='The overall status of the order.') class-attribute instance-attribute
djstripe.models.orders.Order.stripe_class = stripe.Order class-attribute instance-attribute
djstripe.models.orders.Order.stripe_dashboard_item_name = 'orders' class-attribute instance-attribute
djstripe.models.orders.Order.tax_details = JSONField(null=True, blank=True, help_text='Tax details about the purchaser for this order.') class-attribute instance-attribute
djstripe.models.orders.Order.total_details = JSONField(help_text='Tax, discount, and shipping details for the computed total amount of this order.') class-attribute instance-attribute

Functions

djstripe.models.orders.Order.__str__()
Source code in djstripe/models/orders.py
147
148
149
150
151
152
153
154
155
156
157
def __str__(self):
    template = f"on {self.created.strftime('%m/%d/%Y')} ({self.status})"
    if self.status in (OrderStatus.open, OrderStatus.canceled):
        return "Created " + template
    elif self.status in (
        OrderStatus.submitted,
        OrderStatus.complete,
        OrderStatus.processing,
    ):
        return "Placed " + template
    return self.id
djstripe.models.orders.Order.cancel(api_key=None, stripe_account=None, **kwargs)

Cancels the order as well as the payment intent if one is attached.

:param api_key: The api key to use for this request. Defaults to djstripe_settings.STRIPE_SECRET_KEY. :type api_key: string :param stripe_account: The optional connected account for which this request is being made. :type stripe_account: string

Source code in djstripe/models/orders.py
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
def cancel(self, api_key=None, stripe_account=None, **kwargs):
    """
    Cancels the order as well as the payment intent if one is attached.

    :param api_key: The api key to use for this request. \
        Defaults to djstripe_settings.STRIPE_SECRET_KEY.
    :type api_key: string
    :param stripe_account: The optional connected account \
        for which this request is being made.
    :type stripe_account: string
    """
    api_key = api_key or self.default_api_key
    # Prefer passed in stripe_account if set.
    if not stripe_account:
        stripe_account = self._get_stripe_account_id(api_key)

    return self.stripe_class.cancel(
        self.id,
        api_key=api_key,
        stripe_account=stripe_account,
        stripe_version=djstripe_settings.STRIPE_API_VERSION,
        **kwargs,
    )
djstripe.models.orders.Order.reopen(api_key=None, stripe_account=None, **kwargs)

Reopens a submitted order.

:param api_key: The api key to use for this request. Defaults to djstripe_settings.STRIPE_SECRET_KEY. :type api_key: string :param stripe_account: The optional connected account for which this request is being made. :type stripe_account: string

Source code in djstripe/models/orders.py
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
def reopen(self, api_key=None, stripe_account=None, **kwargs):
    """
    Reopens a submitted order.

    :param api_key: The api key to use for this request. \
        Defaults to djstripe_settings.STRIPE_SECRET_KEY.
    :type api_key: string
    :param stripe_account: The optional connected account \
        for which this request is being made.
    :type stripe_account: string
    """
    api_key = api_key or self.default_api_key
    # Prefer passed in stripe_account if set.
    if not stripe_account:
        stripe_account = self._get_stripe_account_id(api_key)

    return self.stripe_class.reopen(
        self.id,
        api_key=api_key,
        stripe_account=stripe_account,
        stripe_version=djstripe_settings.STRIPE_API_VERSION,
        **kwargs,
    )
djstripe.models.orders.Order.submit(api_key=None, stripe_account=None, **kwargs)

Submitting an Order transitions the status to processing and creates a PaymentIntent object so the order can be paid. If the Order has an amount_total of 0, no PaymentIntent object will be created. Once the order is submitted, its contents cannot be changed, unless the reopen method is called.

:param api_key: The api key to use for this request. Defaults to djstripe_settings.STRIPE_SECRET_KEY. :type api_key: string :param stripe_account: The optional connected account for which this request is being made. :type stripe_account: string

Source code in djstripe/models/orders.py
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
def submit(self, api_key=None, stripe_account=None, **kwargs):
    """
    Submitting an Order transitions the status to processing and creates a PaymentIntent object
    so the order can be paid.
    If the Order has an amount_total of 0, no PaymentIntent object will be created.
    Once the order is submitted, its contents cannot be changed,
    unless the reopen method is called.

    :param api_key: The api key to use for this request. \
        Defaults to djstripe_settings.STRIPE_SECRET_KEY.
    :type api_key: string
    :param stripe_account: The optional connected account \
        for which this request is being made.
    :type stripe_account: string
    """
    api_key = api_key or self.default_api_key
    # Prefer passed in stripe_account if set.
    if not stripe_account:
        stripe_account = self._get_stripe_account_id(api_key)

    return self.stripe_class.submit(
        self.id,
        api_key=api_key,
        stripe_account=stripe_account,
        stripe_version=djstripe_settings.STRIPE_API_VERSION,
        **kwargs,
    )

Sigma

Classes

djstripe.models.sigma.ScheduledQueryRun

Bases: StripeModel

Stripe documentation: https://stripe.com/docs/api?lang=python#scheduled_queries

Source code in djstripe/models/sigma.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class ScheduledQueryRun(StripeModel):
    """
    Stripe documentation: https://stripe.com/docs/api?lang=python#scheduled_queries
    """

    stripe_class = stripe.sigma.ScheduledQueryRun

    data_load_time = StripeDateTimeField(
        help_text=(
            "When the query was run, Sigma contained a snapshot of your "
            "Stripe data at this time."
        )
    )
    error = JSONField(
        null=True,
        blank=True,
        help_text=(
            "If the query run was not succeesful, contains information "
            "about the failure."
        ),
    )
    file = StripeForeignKey(
        "file",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        help_text="The file object representing the results of the query.",
    )
    result_available_until = StripeDateTimeField(
        help_text=(
            "Time at which the result expires and is no longer available for download."
        )
    )
    sql = models.TextField(max_length=5000, help_text="SQL for the query.")
    status = StripeEnumField(
        enum=enums.ScheduledQueryRunStatus, help_text="The query's execution status."
    )
    title = models.TextField(max_length=5000, help_text="Title of the query.")

    # TODO Write corresponding test
    def __str__(self):
        return f"{self.title or self.id} ({self.status})"

Attributes

djstripe.models.sigma.ScheduledQueryRun.data_load_time = StripeDateTimeField(help_text='When the query was run, Sigma contained a snapshot of your Stripe data at this time.') class-attribute instance-attribute
djstripe.models.sigma.ScheduledQueryRun.error = JSONField(null=True, blank=True, help_text='If the query run was not succeesful, contains information about the failure.') class-attribute instance-attribute
djstripe.models.sigma.ScheduledQueryRun.file = StripeForeignKey('file', on_delete=models.SET_NULL, null=True, blank=True, help_text='The file object representing the results of the query.') class-attribute instance-attribute
djstripe.models.sigma.ScheduledQueryRun.result_available_until = StripeDateTimeField(help_text='Time at which the result expires and is no longer available for download.') class-attribute instance-attribute
djstripe.models.sigma.ScheduledQueryRun.sql = models.TextField(max_length=5000, help_text='SQL for the query.') class-attribute instance-attribute
djstripe.models.sigma.ScheduledQueryRun.status = StripeEnumField(enum=enums.ScheduledQueryRunStatus, help_text="The query's execution status.") class-attribute instance-attribute
djstripe.models.sigma.ScheduledQueryRun.stripe_class = stripe.sigma.ScheduledQueryRun class-attribute instance-attribute
djstripe.models.sigma.ScheduledQueryRun.title = models.TextField(max_length=5000, help_text='Title of the query.') class-attribute instance-attribute

Functions

djstripe.models.sigma.ScheduledQueryRun.__str__()
Source code in djstripe/models/sigma.py
50
51
def __str__(self):
    return f"{self.title or self.id} ({self.status})"

Webhooks

Module for dj-stripe Webhook models

Attributes

Classes

djstripe.models.webhooks.WebhookEndpoint

Bases: StripeModel

Source code in djstripe/models/webhooks.py
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
class WebhookEndpoint(StripeModel):
    stripe_class = stripe.WebhookEndpoint
    stripe_dashboard_item_name = "webhooks"

    api_version = models.CharField(
        max_length=64,
        blank=True,
        help_text=(
            "The API version events are rendered as for this webhook endpoint. Defaults"
            " to the configured Stripe API Version."
        ),
    )
    enabled_events = JSONField(
        help_text=(
            "The list of events to enable for this endpoint. ['*'] indicates that all"
            " events are enabled, except those that require explicit selection."
        )
    )
    secret = models.CharField(
        max_length=256,
        blank=True,
        editable=False,
        help_text="The endpoint's secret, used to generate webhook signatures.",
    )
    status = StripeEnumField(
        enum=WebhookEndpointStatus,
        help_text="The status of the webhook. It can be enabled or disabled.",
    )
    url = models.URLField(help_text="The URL of the webhook endpoint.", max_length=2048)
    application = models.CharField(
        max_length=255,
        blank=True,
        help_text="The ID of the associated Connect application.",
    )

    djstripe_uuid = models.UUIDField(
        null=True,
        unique=True,
        default=uuid4,
        help_text="A UUID specific to dj-stripe generated for the endpoint",
    )
    tolerance = models.PositiveSmallIntegerField(
        help_text="Controls the milliseconds tolerance which wards against replay attacks. Leave this to its default value unless you know what you're doing.",
        default=djstripe_settings.WEBHOOK_TOLERANCE,
    )

    def __str__(self):
        return self.url or str(self.djstripe_uuid)

    def _attach_objects_hook(
        self, cls, data, current_ids=None, api_key=djstripe_settings.STRIPE_SECRET_KEY
    ):
        """
        Gets called by this object's create and sync methods just before save.
        Use this to populate fields before the model is saved.
        """
        super()._attach_objects_hook(
            cls, data, current_ids=current_ids, api_key=api_key
        )

        self.djstripe_uuid = data.get("metadata", {}).get("djstripe_uuid")
        self.tolerance = data.get("tolerance", djstripe_settings.WEBHOOK_TOLERANCE)

Attributes

djstripe.models.webhooks.WebhookEndpoint.api_version = models.CharField(max_length=64, blank=True, help_text='The API version events are rendered as for this webhook endpoint. Defaults to the configured Stripe API Version.') class-attribute instance-attribute
djstripe.models.webhooks.WebhookEndpoint.application = models.CharField(max_length=255, blank=True, help_text='The ID of the associated Connect application.') class-attribute instance-attribute
djstripe.models.webhooks.WebhookEndpoint.djstripe_uuid = models.UUIDField(null=True, unique=True, default=uuid4, help_text='A UUID specific to dj-stripe generated for the endpoint') class-attribute instance-attribute
djstripe.models.webhooks.WebhookEndpoint.enabled_events = JSONField(help_text="The list of events to enable for this endpoint. ['*'] indicates that all events are enabled, except those that require explicit selection.") class-attribute instance-attribute
djstripe.models.webhooks.WebhookEndpoint.secret = models.CharField(max_length=256, blank=True, editable=False, help_text="The endpoint's secret, used to generate webhook signatures.") class-attribute instance-attribute
djstripe.models.webhooks.WebhookEndpoint.status = StripeEnumField(enum=WebhookEndpointStatus, help_text='The status of the webhook. It can be enabled or disabled.') class-attribute instance-attribute
djstripe.models.webhooks.WebhookEndpoint.stripe_class = stripe.WebhookEndpoint class-attribute instance-attribute
djstripe.models.webhooks.WebhookEndpoint.stripe_dashboard_item_name = 'webhooks' class-attribute instance-attribute
djstripe.models.webhooks.WebhookEndpoint.tolerance = models.PositiveSmallIntegerField(help_text="Controls the milliseconds tolerance which wards against replay attacks. Leave this to its default value unless you know what you're doing.", default=djstripe_settings.WEBHOOK_TOLERANCE) class-attribute instance-attribute
djstripe.models.webhooks.WebhookEndpoint.url = models.URLField(help_text='The URL of the webhook endpoint.', max_length=2048) class-attribute instance-attribute

Functions

djstripe.models.webhooks.WebhookEndpoint.__str__()
Source code in djstripe/models/webhooks.py
71
72
def __str__(self):
    return self.url or str(self.djstripe_uuid)

djstripe.models.webhooks.WebhookEventTrigger

Bases: Model

An instance of a request that reached the server endpoint for Stripe webhooks.

Webhook Events are initially UNTRUSTED, as it is possible for any web entity to post any data to our webhook url. Data posted may be valid Stripe information, garbage, or even malicious. The 'valid' flag in this model monitors this.

Source code in djstripe/models/webhooks.py
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
class WebhookEventTrigger(models.Model):
    """
    An instance of a request that reached the server endpoint for Stripe webhooks.

    Webhook Events are initially **UNTRUSTED**, as it is possible for any web entity to
    post any data to our webhook url. Data posted may be valid Stripe information,
    garbage, or even malicious.
    The 'valid' flag in this model monitors this.
    """

    id = models.BigAutoField(primary_key=True)
    remote_ip = models.GenericIPAddressField(
        help_text="IP address of the request client."
    )
    headers = JSONField()
    body = models.TextField(blank=True)
    valid = models.BooleanField(
        default=False,
        help_text="Whether or not the webhook event has passed validation",
    )
    processed = models.BooleanField(
        default=False,
        help_text="Whether or not the webhook event has been successfully processed",
    )
    exception = models.CharField(max_length=128, blank=True)
    traceback = models.TextField(
        blank=True, help_text="Traceback if an exception was thrown during processing"
    )
    event = StripeForeignKey(
        "Event",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        help_text="Event object contained in the (valid) Webhook",
    )
    djstripe_version = models.CharField(
        max_length=32,
        default=_get_version,  # Needs to be a callable, otherwise it's a db default.
        help_text="The version of dj-stripe when the webhook was received",
    )
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    stripe_trigger_account = StripeForeignKey(
        "djstripe.Account",
        on_delete=models.CASCADE,
        to_field="id",
        null=True,
        blank=True,
        help_text="The Stripe Account this object belongs to.",
    )
    webhook_endpoint = StripeForeignKey(
        "WebhookEndpoint",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        help_text="The endpoint this webhook was received on",
    )

    def __str__(self):
        return f"id={self.id}, valid={self.valid}, processed={self.processed}"

    @classmethod
    def from_request(cls, request, *, webhook_endpoint: WebhookEndpoint = None):
        """
        Create, validate and process a WebhookEventTrigger given a Django
        request object.

        The process is three-fold:
        1. Create a WebhookEventTrigger object from a Django request.
        2. Validate the WebhookEventTrigger as a Stripe event using the API.
        3. If valid, process it into an Event object (and child resource).
        """

        try:
            body = request.body.decode(request.encoding or "utf-8")
        except Exception:
            body = "(error decoding body)"

        ip = get_remote_ip(request)

        try:
            data = json.loads(body)
        except ValueError:
            data = {}

        if webhook_endpoint is None:
            stripe_account = StripeModel._find_owner_account(data=data)
            secret = djstripe_settings.WEBHOOK_SECRET
        else:
            stripe_account = webhook_endpoint.djstripe_owner_account
            secret = webhook_endpoint.secret

        obj = cls.objects.create(
            headers=dict(request.headers),
            body=body,
            remote_ip=ip,
            stripe_trigger_account=stripe_account,
            webhook_endpoint=webhook_endpoint,
        )
        api_key = (
            stripe_account.default_api_key
            or djstripe_settings.get_default_api_key(obj.livemode)
        )

        try:
            # Validate the webhook first
            signals.webhook_pre_validate.send(sender=cls, instance=obj)

            if webhook_endpoint:
                # Default to per Webhook Endpoint Tolerance
                obj.valid = obj.validate(
                    secret=secret,
                    api_key=api_key,
                    tolerance=webhook_endpoint.tolerance,
                )
            else:
                obj.valid = obj.validate(secret=secret, api_key=api_key)
            signals.webhook_post_validate.send(
                sender=cls, instance=obj, valid=obj.valid
            )

            if obj.valid:
                signals.webhook_pre_process.send(sender=cls, instance=obj)
                if djstripe_settings.WEBHOOK_EVENT_CALLBACK:
                    # If WEBHOOK_EVENT_CALLBACK, pass it for processing
                    djstripe_settings.WEBHOOK_EVENT_CALLBACK(obj, api_key=api_key)
                else:
                    # Process the item (do not save it, it'll get saved below)
                    obj.process(save=False, api_key=api_key)
                signals.webhook_post_process.send(
                    sender=cls, instance=obj, api_key=api_key
                )
        except Exception as e:
            max_length = cls._meta.get_field("exception").max_length
            obj.exception = str(e)[:max_length]
            obj.traceback = format_exc()

            # Send the exception as the webhook_processing_error signal
            signals.webhook_processing_error.send(
                sender=cls,
                instance=obj,
                api_key=api_key,
                exception=e,
                data=getattr(e, "http_body", ""),
            )

            # re-raise the exception so Django sees it
            raise e
        finally:
            obj.save()

        return obj

    @cached_property
    def json_body(self):
        try:
            return json.loads(self.body)
        except ValueError:
            return {}

    @property
    def is_test_event(self):
        event_id = self.json_body.get("id")
        return event_id and event_id.endswith("_00000000000000")

    def verify_signature(
        self, secret: str, tolerance: int = djstripe_settings.WEBHOOK_TOLERANCE
    ) -> bool:
        if not secret:
            raise ValueError("Cannot verify event signature without a secret")

        # HTTP headers are case-insensitive, but we store them as a dict.
        headers = CaseInsensitiveMapping(self.headers)
        signature = headers.get("stripe-signature")

        try:
            stripe.WebhookSignature.verify_header(
                self.body, signature, secret, tolerance
            )
        except stripe.error.SignatureVerificationError:
            logger.exception("Failed to verify header")
            return False
        else:
            return True

    def validate(
        self,
        api_key: str = None,
        secret: str = djstripe_settings.WEBHOOK_SECRET,
        tolerance: int = djstripe_settings.WEBHOOK_TOLERANCE,
        validation_method=djstripe_settings.WEBHOOK_VALIDATION,
    ):
        """
        The original contents of the Event message must be confirmed by
        refetching it and comparing the fetched data with the original data.

        This function makes an API call to Stripe to redownload the Event data
        and returns whether or not it matches the WebhookEventTrigger data.
        """

        local_data = self.json_body
        if "id" not in local_data or "livemode" not in local_data:
            logger.error(
                '"id" not in json body or "livemode" not in json body(%s)', local_data
            )
            return False

        if self.is_test_event:
            logger.info("Test webhook received and discarded: %s", local_data)
            return False

        if validation_method is None:
            # validation disabled
            warnings.warn("WEBHOOK VALIDATION is disabled.")
            return True
        elif validation_method == "verify_signature":
            if settings.DEBUG:
                # In debug mode, allow overriding the webhook secret with
                # the x-djstripe-webhook-secret header.
                # (used for stripe cli webhook forwarding)
                headers = CaseInsensitiveMapping(self.headers)
                local_secret = headers.get("x-djstripe-webhook-secret")
                secret = local_secret if local_secret else secret
            return self.verify_signature(secret=secret, tolerance=tolerance)

        livemode = local_data["livemode"]
        api_key = api_key or djstripe_settings.get_default_api_key(livemode)

        # Retrieve the event using the api_version specified in itself
        remote_data = Event.stripe_class.retrieve(
            id=local_data["id"],
            api_key=api_key,
            stripe_version=local_data["api_version"],
        )

        return local_data["data"] == remote_data["data"]

    def process(self, save=True, api_key: str = None):
        # Reset traceback and exception in case of reprocessing
        self.exception = ""
        self.traceback = ""

        self.event = Event.process(self.json_body, api_key=api_key)
        self.processed = True
        if save:
            self.save()

        return self.event

Attributes

djstripe.models.webhooks.WebhookEventTrigger.body = models.TextField(blank=True) class-attribute instance-attribute
djstripe.models.webhooks.WebhookEventTrigger.created = models.DateTimeField(auto_now_add=True) class-attribute instance-attribute
djstripe.models.webhooks.WebhookEventTrigger.djstripe_version = models.CharField(max_length=32, default=_get_version, help_text='The version of dj-stripe when the webhook was received') class-attribute instance-attribute
djstripe.models.webhooks.WebhookEventTrigger.event = StripeForeignKey('Event', on_delete=models.SET_NULL, null=True, blank=True, help_text='Event object contained in the (valid) Webhook') class-attribute instance-attribute
djstripe.models.webhooks.WebhookEventTrigger.exception = models.CharField(max_length=128, blank=True) class-attribute instance-attribute
djstripe.models.webhooks.WebhookEventTrigger.headers = JSONField() class-attribute instance-attribute
djstripe.models.webhooks.WebhookEventTrigger.id = models.BigAutoField(primary_key=True) class-attribute instance-attribute
djstripe.models.webhooks.WebhookEventTrigger.is_test_event property
djstripe.models.webhooks.WebhookEventTrigger.processed = models.BooleanField(default=False, help_text='Whether or not the webhook event has been successfully processed') class-attribute instance-attribute
djstripe.models.webhooks.WebhookEventTrigger.remote_ip = models.GenericIPAddressField(help_text='IP address of the request client.') class-attribute instance-attribute
djstripe.models.webhooks.WebhookEventTrigger.stripe_trigger_account = StripeForeignKey('djstripe.Account', on_delete=models.CASCADE, to_field='id', null=True, blank=True, help_text='The Stripe Account this object belongs to.') class-attribute instance-attribute
djstripe.models.webhooks.WebhookEventTrigger.traceback = models.TextField(blank=True, help_text='Traceback if an exception was thrown during processing') class-attribute instance-attribute
djstripe.models.webhooks.WebhookEventTrigger.updated = models.DateTimeField(auto_now=True) class-attribute instance-attribute
djstripe.models.webhooks.WebhookEventTrigger.valid = models.BooleanField(default=False, help_text='Whether or not the webhook event has passed validation') class-attribute instance-attribute
djstripe.models.webhooks.WebhookEventTrigger.webhook_endpoint = StripeForeignKey('WebhookEndpoint', on_delete=models.SET_NULL, null=True, blank=True, help_text='The endpoint this webhook was received on') class-attribute instance-attribute

Functions

djstripe.models.webhooks.WebhookEventTrigger.__str__()
Source code in djstripe/models/webhooks.py
179
180
def __str__(self):
    return f"id={self.id}, valid={self.valid}, processed={self.processed}"
djstripe.models.webhooks.WebhookEventTrigger.from_request(request, *, webhook_endpoint=None) classmethod

Create, validate and process a WebhookEventTrigger given a Django request object.

The process is three-fold: 1. Create a WebhookEventTrigger object from a Django request. 2. Validate the WebhookEventTrigger as a Stripe event using the API. 3. If valid, process it into an Event object (and child resource).

Source code in djstripe/models/webhooks.py
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
@classmethod
def from_request(cls, request, *, webhook_endpoint: WebhookEndpoint = None):
    """
    Create, validate and process a WebhookEventTrigger given a Django
    request object.

    The process is three-fold:
    1. Create a WebhookEventTrigger object from a Django request.
    2. Validate the WebhookEventTrigger as a Stripe event using the API.
    3. If valid, process it into an Event object (and child resource).
    """

    try:
        body = request.body.decode(request.encoding or "utf-8")
    except Exception:
        body = "(error decoding body)"

    ip = get_remote_ip(request)

    try:
        data = json.loads(body)
    except ValueError:
        data = {}

    if webhook_endpoint is None:
        stripe_account = StripeModel._find_owner_account(data=data)
        secret = djstripe_settings.WEBHOOK_SECRET
    else:
        stripe_account = webhook_endpoint.djstripe_owner_account
        secret = webhook_endpoint.secret

    obj = cls.objects.create(
        headers=dict(request.headers),
        body=body,
        remote_ip=ip,
        stripe_trigger_account=stripe_account,
        webhook_endpoint=webhook_endpoint,
    )
    api_key = (
        stripe_account.default_api_key
        or djstripe_settings.get_default_api_key(obj.livemode)
    )

    try:
        # Validate the webhook first
        signals.webhook_pre_validate.send(sender=cls, instance=obj)

        if webhook_endpoint:
            # Default to per Webhook Endpoint Tolerance
            obj.valid = obj.validate(
                secret=secret,
                api_key=api_key,
                tolerance=webhook_endpoint.tolerance,
            )
        else:
            obj.valid = obj.validate(secret=secret, api_key=api_key)
        signals.webhook_post_validate.send(
            sender=cls, instance=obj, valid=obj.valid
        )

        if obj.valid:
            signals.webhook_pre_process.send(sender=cls, instance=obj)
            if djstripe_settings.WEBHOOK_EVENT_CALLBACK:
                # If WEBHOOK_EVENT_CALLBACK, pass it for processing
                djstripe_settings.WEBHOOK_EVENT_CALLBACK(obj, api_key=api_key)
            else:
                # Process the item (do not save it, it'll get saved below)
                obj.process(save=False, api_key=api_key)
            signals.webhook_post_process.send(
                sender=cls, instance=obj, api_key=api_key
            )
    except Exception as e:
        max_length = cls._meta.get_field("exception").max_length
        obj.exception = str(e)[:max_length]
        obj.traceback = format_exc()

        # Send the exception as the webhook_processing_error signal
        signals.webhook_processing_error.send(
            sender=cls,
            instance=obj,
            api_key=api_key,
            exception=e,
            data=getattr(e, "http_body", ""),
        )

        # re-raise the exception so Django sees it
        raise e
    finally:
        obj.save()

    return obj
djstripe.models.webhooks.WebhookEventTrigger.json_body()
Source code in djstripe/models/webhooks.py
274
275
276
277
278
279
@cached_property
def json_body(self):
    try:
        return json.loads(self.body)
    except ValueError:
        return {}
djstripe.models.webhooks.WebhookEventTrigger.process(save=True, api_key=None)
Source code in djstripe/models/webhooks.py
358
359
360
361
362
363
364
365
366
367
368
def process(self, save=True, api_key: str = None):
    # Reset traceback and exception in case of reprocessing
    self.exception = ""
    self.traceback = ""

    self.event = Event.process(self.json_body, api_key=api_key)
    self.processed = True
    if save:
        self.save()

    return self.event
djstripe.models.webhooks.WebhookEventTrigger.validate(api_key=None, secret=djstripe_settings.WEBHOOK_SECRET, tolerance=djstripe_settings.WEBHOOK_TOLERANCE, validation_method=djstripe_settings.WEBHOOK_VALIDATION)

The original contents of the Event message must be confirmed by refetching it and comparing the fetched data with the original data.

This function makes an API call to Stripe to redownload the Event data and returns whether or not it matches the WebhookEventTrigger data.

Source code in djstripe/models/webhooks.py
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
def validate(
    self,
    api_key: str = None,
    secret: str = djstripe_settings.WEBHOOK_SECRET,
    tolerance: int = djstripe_settings.WEBHOOK_TOLERANCE,
    validation_method=djstripe_settings.WEBHOOK_VALIDATION,
):
    """
    The original contents of the Event message must be confirmed by
    refetching it and comparing the fetched data with the original data.

    This function makes an API call to Stripe to redownload the Event data
    and returns whether or not it matches the WebhookEventTrigger data.
    """

    local_data = self.json_body
    if "id" not in local_data or "livemode" not in local_data:
        logger.error(
            '"id" not in json body or "livemode" not in json body(%s)', local_data
        )
        return False

    if self.is_test_event:
        logger.info("Test webhook received and discarded: %s", local_data)
        return False

    if validation_method is None:
        # validation disabled
        warnings.warn("WEBHOOK VALIDATION is disabled.")
        return True
    elif validation_method == "verify_signature":
        if settings.DEBUG:
            # In debug mode, allow overriding the webhook secret with
            # the x-djstripe-webhook-secret header.
            # (used for stripe cli webhook forwarding)
            headers = CaseInsensitiveMapping(self.headers)
            local_secret = headers.get("x-djstripe-webhook-secret")
            secret = local_secret if local_secret else secret
        return self.verify_signature(secret=secret, tolerance=tolerance)

    livemode = local_data["livemode"]
    api_key = api_key or djstripe_settings.get_default_api_key(livemode)

    # Retrieve the event using the api_version specified in itself
    remote_data = Event.stripe_class.retrieve(
        id=local_data["id"],
        api_key=api_key,
        stripe_version=local_data["api_version"],
    )

    return local_data["data"] == remote_data["data"]
djstripe.models.webhooks.WebhookEventTrigger.verify_signature(secret, tolerance=djstripe_settings.WEBHOOK_TOLERANCE)
Source code in djstripe/models/webhooks.py
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
def verify_signature(
    self, secret: str, tolerance: int = djstripe_settings.WEBHOOK_TOLERANCE
) -> bool:
    if not secret:
        raise ValueError("Cannot verify event signature without a secret")

    # HTTP headers are case-insensitive, but we store them as a dict.
    headers = CaseInsensitiveMapping(self.headers)
    signature = headers.get("stripe-signature")

    try:
        stripe.WebhookSignature.verify_header(
            self.body, signature, secret, tolerance
        )
    except stripe.error.SignatureVerificationError:
        logger.exception("Failed to verify header")
        return False
    else:
        return True

Functions

djstripe.models.webhooks.get_remote_ip(request)

Given the HTTPRequest object return the IP Address of the client

:param request: client request :type request: HTTPRequest

:Returns: the client ip address

Source code in djstripe/models/webhooks.py
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
def get_remote_ip(request):
    """Given the HTTPRequest object return the IP Address of the client

    :param request: client request
    :type request: HTTPRequest

    :Returns: the client ip address
    """

    # x-forwarded-for is relevant for django running behind a proxy
    x_forwarded_for = request.headers.get("x-forwarded-for")
    if x_forwarded_for:
        ip = x_forwarded_for.split(",")[0]
    else:
        ip = request.META.get("REMOTE_ADDR")

    if not ip:
        warnings.warn(
            "Could not determine remote IP (missing REMOTE_ADDR). "
            "This is likely an issue with your wsgi/server setup."
        )
        ip = "0.0.0.0"

    return ip