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
 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
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
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
djstripe.models.core.BalanceTransaction.currency = StripeCurrencyCodeField() class-attribute
djstripe.models.core.BalanceTransaction.exchange_rate = models.DecimalField(null=True, decimal_places=6, max_digits=8) class-attribute
djstripe.models.core.BalanceTransaction.fee = StripeQuantumCurrencyAmountField(help_text='Fee (in cents) paid for this transaction.') class-attribute
djstripe.models.core.BalanceTransaction.fee_details = JSONField() class-attribute
djstripe.models.core.BalanceTransaction.net = StripeQuantumCurrencyAmountField(help_text='Net amount of the transaction, in cents.') class-attribute
djstripe.models.core.BalanceTransaction.reporting_category = StripeEnumField(enum=enums.BalanceTransactionReportingCategory, help_text='More information: https://stripe.com/docs/reports/reporting-categories') class-attribute
djstripe.models.core.BalanceTransaction.source = StripeIdField() class-attribute
djstripe.models.core.BalanceTransaction.status = StripeEnumField(enum=enums.BalanceTransactionStatus) class-attribute
djstripe.models.core.BalanceTransaction.stripe_class = stripe.BalanceTransaction class-attribute
djstripe.models.core.BalanceTransaction.type = StripeEnumField(enum=enums.BalanceTransactionType) class-attribute

Functions

djstripe.models.core.BalanceTransaction.__str__()
Source code in djstripe/models/core.py
94
95
96
97
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
 99
100
101
102
103
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
105
106
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
108
109
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
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
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) -> "Charge":
        """
        Initiate a refund. Returns the charge 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.
        """
        charge_obj = self.api_retrieve().refund(
            amount=self._calculate_refund_amount(amount=amount), reason=reason
        )
        return self.__class__.sync_from_stripe_data(
            charge_obj, api_key=self.default_api_key
        )

    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
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
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
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
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
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
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
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
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
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
djstripe.models.core.Charge.currency = StripeCurrencyCodeField(help_text='The currency in which the charge was made.') class-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
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
djstripe.models.core.Charge.disputed = models.BooleanField(default=False, help_text='Whether the charge has been disputed.') class-attribute
djstripe.models.core.Charge.expand_fields = ['balance_transaction'] class-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
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
djstripe.models.core.Charge.fraud_details = JSONField(help_text='Hash with information on fraud assessments for the charge.', null=True, blank=True) class-attribute
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
djstripe.models.core.Charge.objects = ChargeManager() class-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
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
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
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
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
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
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
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
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
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
djstripe.models.core.Charge.shipping = JSONField(null=True, blank=True, help_text='Shipping information for the charge') class-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
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
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
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
djstripe.models.core.Charge.status = StripeEnumField(enum=enums.ChargeStatus, help_text='The status of the payment.') class-attribute
djstripe.models.core.Charge.stripe_class = stripe.Charge class-attribute
djstripe.models.core.Charge.stripe_dashboard_item_name = 'payments' class-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
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
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

Functions

djstripe.models.core.Charge.__str__()
Source code in djstripe/models/core.py
368
369
370
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
424
425
426
427
428
429
430
431
432
433
434
435
436
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.fee() property
Source code in djstripe/models/core.py
372
373
374
375
@property
def fee(self):
    if self.balance_transaction:
        return self.balance_transaction.fee
djstripe.models.core.Charge.fraudulent() property
Source code in djstripe/models/core.py
387
388
389
390
391
@property
def fraudulent(self) -> bool:
    return (
        self.fraud_details and list(self.fraud_details.values())[0] == "fraudulent"
    )
djstripe.models.core.Charge.human_readable_status() property
Source code in djstripe/models/core.py
377
378
379
380
381
382
383
384
385
@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)
djstripe.models.core.Charge.refund(amount=None, reason=None)

Initiate a refund. Returns the charge 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
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
def refund(self, amount: Decimal = None, reason: str = None) -> "Charge":
    """
    Initiate a refund. Returns the charge 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.
    """
    charge_obj = self.api_retrieve().refund(
        amount=self._calculate_refund_amount(amount=amount), reason=reason
    )
    return self.__class__.sync_from_stripe_data(
        charge_obj, api_key=self.default_api_key
    )

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
 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
 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
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>
    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 Customer.objects.get(subscriber=subscriber, livemode=livemode), False
        except Customer.DoesNotExist:
            action = "create:{}".format(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

        stripe_customer = cls._api_create(
            email=subscriber.email,
            idempotency_key=idempotency_key,
            metadata=metadata,
            stripe_account=stripe_account,
        )
        customer, created = Customer.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 = "customer:create:{}".format(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 can_charge(self):
        """Determines if this customer is able to be charged."""

        warnings.warn(
            "Customer.can_charge() is misleading and deprecated, will be removed in dj-stripe 2.8. "
            "Look at Customer.payment_methods.all() instead.",
            DeprecationWarning,
        )

        return (
            self.has_valid_source() or self.default_payment_method is not None
        ) and self.date_purged is None

    def send_invoice(self):
        """
        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()
            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):
        """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()  # Always retry unpaid invoices
            except InvalidRequestError as exc:
                if str(exc) != "Invoice is already paid":
                    raise

    def has_valid_source(self):
        """Check whether the customer has a valid payment source."""
        warnings.warn(
            "Customer.has_valid_source() is deprecated and will be removed in dj-stripe 2.8. "
            "Use `Customer.default_source is not None` instead.",
            DeprecationWarning,
        )
        return self.default_source is not None

    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.address = JSONField(null=True, blank=True, help_text="The customer's address.") class-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
djstripe.models.core.Customer.coupon = models.ForeignKey('Coupon', null=True, blank=True, on_delete=models.SET_NULL) class-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
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
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
djstripe.models.core.Customer.date_purged = models.DateTimeField(null=True, editable=False) class-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
djstripe.models.core.Customer.default_source = PaymentMethodForeignKey(on_delete=models.SET_NULL, null=True, blank=True, related_name='customers') class-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
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
djstripe.models.core.Customer.email = models.TextField(max_length=5000, default='', blank=True) class-attribute
djstripe.models.core.Customer.expand_fields = ['default_source', 'sources'] class-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
djstripe.models.core.Customer.invoice_settings = JSONField(null=True, blank=True, help_text="The customer's default invoice settings.") class-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
djstripe.models.core.Customer.phone = models.TextField(max_length=5000, default='', blank=True, help_text="The customer's phone number.") class-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
djstripe.models.core.Customer.shipping = JSONField(null=True, blank=True, help_text='Shipping information associated with the customer.') class-attribute
djstripe.models.core.Customer.stripe_class = stripe.Customer class-attribute
djstripe.models.core.Customer.stripe_dashboard_item_name = 'customers' class-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
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

Classes

djstripe.models.core.Customer.Meta

Bases: StripeModel.Meta

Source code in djstripe/models/core.py
754
755
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

Functions

djstripe.models.core.Customer.__str__()
Source code in djstripe/models/core.py
757
758
759
760
761
def __str__(self):
    if self.subscriber:
        return str(self.subscriber)

    return self.name or self.description or self.id
djstripe.models.core.Customer.active_subscriptions() property

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

Source code in djstripe/models/core.py
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
@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(),
    )
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
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
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
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
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
 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
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
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
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.can_charge()

Determines if this customer is able to be charged.

Source code in djstripe/models/core.py
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
def can_charge(self):
    """Determines if this customer is able to be charged."""

    warnings.warn(
        "Customer.can_charge() is misleading and deprecated, will be removed in dj-stripe 2.8. "
        "Look at Customer.payment_methods.all() instead.",
        DeprecationWarning,
    )

    return (
        self.has_valid_source() or self.default_payment_method is not None
    ) and self.date_purged is None
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
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
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
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
@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

    stripe_customer = cls._api_create(
        email=subscriber.email,
        idempotency_key=idempotency_key,
        metadata=metadata,
        stripe_account=stripe_account,
    )
    customer, created = Customer.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.credits() property

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

Source code in djstripe/models/core.py
847
848
849
850
851
852
@property
def credits(self):
    """
    The customer is considered to have credits if their balance is below 0.
    """
    return abs(min(self.balance, 0))
djstripe.models.core.Customer.customer_payment_methods() property

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

Source code in djstripe/models/core.py
854
855
856
857
858
859
860
861
862
863
864
@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
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
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
@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 Customer.objects.get(subscriber=subscriber, livemode=livemode), False
    except Customer.DoesNotExist:
        action = "create:{}".format(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
1159
1160
1161
1162
1163
1164
1165
1166
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.has_valid_source()

Check whether the customer has a valid payment source.

Source code in djstripe/models/core.py
1255
1256
1257
1258
1259
1260
1261
1262
def has_valid_source(self):
    """Check whether the customer has a valid payment source."""
    warnings.warn(
        "Customer.has_valid_source() is deprecated and will be removed in dj-stripe 2.8. "
        "Use `Customer.default_source is not None` instead.",
        DeprecationWarning,
    )
    return self.default_source is not None
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
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
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.pending_charges() property

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

Source code in djstripe/models/core.py
866
867
868
869
870
871
@property
def pending_charges(self):
    """
    The customer is considered to have pending charges if their balance is above 0.
    """
    return max(self.balance, 0)
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
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
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 = "customer:create:{}".format(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()

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

Source code in djstripe/models/core.py
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
def retry_unpaid_invoices(self):
    """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()  # Always retry unpaid invoices
        except InvalidRequestError as exc:
            if str(exc) != "Invoice is already paid":
                raise
djstripe.models.core.Customer.send_invoice()

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
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
def send_invoice(self):
    """
    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()
        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
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
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.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.

Source code in djstripe/models/core.py
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
@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()
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
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
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.Customer.valid_subscriptions() property

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

Source code in djstripe/models/core.py
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
@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,
        ]
    )

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
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
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
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
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
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
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
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
djstripe.models.core.Dispute.currency = StripeCurrencyCodeField() class-attribute
djstripe.models.core.Dispute.evidence = JSONField(help_text='Evidence provided to respond to a dispute.') class-attribute
djstripe.models.core.Dispute.evidence_details = JSONField(help_text='Information about the evidence submission.') class-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
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
djstripe.models.core.Dispute.reason = StripeEnumField(enum=enums.DisputeReason) class-attribute
djstripe.models.core.Dispute.status = StripeEnumField(enum=enums.DisputeStatus) class-attribute
djstripe.models.core.Dispute.stripe_class = stripe.Dispute class-attribute
djstripe.models.core.Dispute.stripe_dashboard_item_name = 'payments' class-attribute

Functions

djstripe.models.core.Dispute.__str__()
Source code in djstripe/models/core.py
1452
1453
1454
1455
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
1457
1458
1459
1460
1461
1462
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
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
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 = data.get("id")
        else:
            customer_id = 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
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
djstripe.models.core.Event.idempotency_key = models.TextField(default='', blank=True) class-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
djstripe.models.core.Event.stripe_class = stripe.Event class-attribute
djstripe.models.core.Event.stripe_dashboard_item_name = 'events' class-attribute
djstripe.models.core.Event.type = models.CharField(max_length=250, help_text="Stripe's event description code") class-attribute

Functions

djstripe.models.core.Event.__str__()
Source code in djstripe/models/core.py
1547
1548
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
1604
1605
1606
1607
@cached_property
def category(self):
    """Gets the event category string (e.g. 'customer')."""
    return self.parts[0]
djstripe.models.core.Event.customer() property
Source code in djstripe/models/core.py
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
@property
def customer(self):
    data = self.data["object"]
    if data["object"] == "customer":
        customer_id = data.get("id")
    else:
        customer_id = 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,
        )
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
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
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
1599
1600
1601
1602
@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
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
@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
1609
1610
1611
1612
@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
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
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
djstripe.models.core.File.purpose = StripeEnumField(enum=enums.FilePurpose, help_text='The purpose of the uploaded file.') class-attribute
djstripe.models.core.File.size = models.IntegerField(help_text='The size in bytes of the file upload object.') class-attribute
djstripe.models.core.File.stripe_class = stripe.File class-attribute
djstripe.models.core.File.type = StripeEnumField(enum=enums.FileType, help_text='The type of the file returned.') class-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

Functions

djstripe.models.core.File.__str__()
Source code in djstripe/models/core.py
1662
1663
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
1658
1659
1660
@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
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
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
djstripe.models.core.FileLink.file = StripeForeignKey('File', on_delete=models.CASCADE) class-attribute
djstripe.models.core.FileLink.stripe_class = stripe.FileLink class-attribute
djstripe.models.core.FileLink.url = models.URLField(help_text='The publicly accessible URL to download the file.') class-attribute
djstripe.models.core.FileLink.__str__()
Source code in djstripe/models/core.py
1689
1690
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
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
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
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
djstripe.models.core.Mandate.payment_method = StripeForeignKey('paymentmethod', on_delete=models.CASCADE) class-attribute
djstripe.models.core.Mandate.payment_method_details = JSONField(help_text='Additional mandate information specific to the payment method type.') class-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
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
djstripe.models.core.Mandate.stripe_class = stripe.Mandate class-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

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
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
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
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
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
djstripe.models.core.PaymentIntent.amount_capturable = StripeQuantumCurrencyAmountField(help_text='Amount (in cents) that can be captured from this PaymentIntent.') class-attribute
djstripe.models.core.PaymentIntent.amount_received = StripeQuantumCurrencyAmountField(help_text='Amount (in cents) that was collected by this PaymentIntent.') class-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
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
djstripe.models.core.PaymentIntent.capture_method = StripeEnumField(enum=enums.CaptureMethod, help_text='Capture method of this PaymentIntent, one of automatic or manual.') class-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
djstripe.models.core.PaymentIntent.confirmation_method = StripeEnumField(enum=enums.ConfirmationMethod, help_text='Confirmation method of this PaymentIntent, one of manual or automatic.') class-attribute
djstripe.models.core.PaymentIntent.currency = StripeCurrencyCodeField() class-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
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
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
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
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
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
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
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
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
djstripe.models.core.PaymentIntent.shipping = JSONField(null=True, blank=True, help_text='Shipping information for this PaymentIntent.') class-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
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
djstripe.models.core.PaymentIntent.stripe_class = stripe.PaymentIntent class-attribute
djstripe.models.core.PaymentIntent.stripe_dashboard_item_name = 'payments' class-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
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

Functions

djstripe.models.core.PaymentIntent.__str__()
Source code in djstripe/models/core.py
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
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
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
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
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
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."
        ),
    )
    # TODO: `original_payout` impl as OneToOne, with `reversed_by` reverse relation
    # original_payout = StripeForeignKey(
    #     "Payout",
    #     on_delete=models.SET_NULL,
    #     null=True,
    #     blank=True,
    #     help_text="If the payout reverses another, this is the original 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
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
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
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
djstripe.models.core.Payout.currency = StripeCurrencyCodeField() class-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
djstripe.models.core.Payout.expand_fields = ['destination'] class-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
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
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
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
djstripe.models.core.Payout.source_type = StripeEnumField(enum=enums.PayoutSourceType, help_text='The source balance this payout came from.') class-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
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
djstripe.models.core.Payout.stripe_class = stripe.Payout class-attribute
djstripe.models.core.Payout.stripe_dashboard_item_name = 'payouts' class-attribute
djstripe.models.core.Payout.type = StripeEnumField(enum=enums.PayoutType) class-attribute

Functions

djstripe.models.core.Payout.__str__()
Source code in djstripe/models/core.py
2159
2160
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
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
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
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
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 Price.objects.get(id=kwargs["id"]), False
        except Price.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
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
djstripe.models.core.Price.currency = StripeCurrencyCodeField() class-attribute
djstripe.models.core.Price.expand_fields = ['product', 'tiers'] class-attribute
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
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
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
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
djstripe.models.core.Price.stripe_class = stripe.Price class-attribute
djstripe.models.core.Price.stripe_dashboard_item_name = 'prices' class-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
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
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
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
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
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

Classes

djstripe.models.core.Price.Meta

Bases: object

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

Functions

djstripe.models.core.Price.__str__()
Source code in djstripe/models/core.py
2313
2314
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
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
@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
2287
2288
2289
2290
2291
2292
2293
2294
@classmethod
def get_or_create(cls, **kwargs):
    """Get or create a Price."""

    try:
        return Price.objects.get(id=kwargs["id"]), False
    except Price.DoesNotExist:
        return cls.create(**kwargs), True
djstripe.models.core.Price.human_readable_price() property
Source code in djstripe/models/core.py
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
@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)

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
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
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."
        ),
    )
    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
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
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
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
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
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
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
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
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
djstripe.models.core.Product.stripe_class = stripe.Product class-attribute
djstripe.models.core.Product.stripe_dashboard_item_name = 'products' class-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
djstripe.models.core.Product.unit_label = models.CharField(max_length=12, default='', blank=True) class-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

Functions

djstripe.models.core.Product.__str__()
Source code in djstripe/models/core.py
603
604
605
606
607
608
609
610
611
612
613
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
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
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
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
djstripe.models.core.Refund.charge = StripeForeignKey('Charge', on_delete=models.CASCADE, related_name='refunds', help_text='The charge that was refunded') class-attribute
djstripe.models.core.Refund.currency = StripeCurrencyCodeField() class-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
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
djstripe.models.core.Refund.reason = StripeEnumField(enum=enums.RefundReason, blank=True, default='', help_text='Reason for the refund.') class-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
djstripe.models.core.Refund.status = StripeEnumField(blank=True, enum=enums.RefundStatus, help_text='Status of the refund.') class-attribute
djstripe.models.core.Refund.stripe_class = stripe.Refund class-attribute

Functions

djstripe.models.core.Refund.__str__()
Source code in djstripe/models/core.py
2430
2431
2432
2433
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
2427
2428
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
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
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} "
                f"by {customer}"
            )

        if account:
            return f"{self.payment_method} for {account}. {enums.SetupIntentStatus.humanize(self.status)}"
        if customer:
            return f"{self.payment_method} by {customer}. {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
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
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
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
djstripe.models.core.SetupIntent.last_setup_error = JSONField(null=True, blank=True, help_text='The error encountered in the previous SetupIntent confirmation.') class-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
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
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
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
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
djstripe.models.core.SetupIntent.stripe_class = stripe.SetupIntent class-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

Functions

djstripe.models.core.SetupIntent.__str__()
Source code in djstripe/models/core.py
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
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} "
            f"by {customer}"
        )

    if account:
        return f"{self.payment_method} for {account}. {enums.SetupIntentStatus.humanize(self.status)}"
    if customer:
        return f"{self.payment_method} by {customer}. {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
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
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}) {'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
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
djstripe.models.payment_methods.BankAccount.account_holder_type = StripeEnumField(enum=enums.BankAccountHolderType, help_text='The type of entity that holds the account.') class-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
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
djstripe.models.payment_methods.BankAccount.currency = StripeCurrencyCodeField() class-attribute
djstripe.models.payment_methods.BankAccount.customer = StripeForeignKey('Customer', on_delete=models.SET_NULL, null=True, related_name='bank_account') class-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
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
djstripe.models.payment_methods.BankAccount.last4 = models.CharField(max_length=4) class-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
djstripe.models.payment_methods.BankAccount.status = StripeEnumField(enum=enums.BankAccountStatus) class-attribute
djstripe.models.payment_methods.BankAccount.stripe_class = stripe.BankAccount class-attribute

Functions

djstripe.models.payment_methods.BankAccount.__str__()
Source code in djstripe/models/payment_methods.py
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
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}) {'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
453
454
455
456
457
458
459
460
461
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.BankAccount.human_readable_status() property
Source code in djstripe/models/payment_methods.py
447
448
449
450
451
@property
def human_readable_status(self):
    if self.status == "new":
        return "Pending Verification"
    return enums.BankAccountStatus.humanize(self.status)

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
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
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 {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
djstripe.models.payment_methods.Card.address_city = models.TextField(max_length=5000, blank=True, default='', help_text='City/District/Suburb/Town/Village.') class-attribute
djstripe.models.payment_methods.Card.address_country = models.TextField(max_length=5000, blank=True, default='', help_text='Billing address country.') class-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
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
djstripe.models.payment_methods.Card.address_line2 = models.TextField(max_length=5000, blank=True, default='', help_text='Apartment/Suite/Unit/Building.') class-attribute
djstripe.models.payment_methods.Card.address_state = models.TextField(max_length=5000, blank=True, default='', help_text='State/County/Province/Region.') class-attribute
djstripe.models.payment_methods.Card.address_zip = models.TextField(max_length=5000, blank=True, default='', help_text='ZIP or postal code.') class-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
djstripe.models.payment_methods.Card.brand = StripeEnumField(enum=enums.CardBrand, help_text='Card brand.') class-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
djstripe.models.payment_methods.Card.customer = StripeForeignKey('Customer', on_delete=models.SET_NULL, null=True, related_name='legacy_cards') class-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
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
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
djstripe.models.payment_methods.Card.exp_month = models.IntegerField(help_text='Card expiration month.') class-attribute
djstripe.models.payment_methods.Card.exp_year = models.IntegerField(help_text='Card expiration year.') class-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
djstripe.models.payment_methods.Card.funding = StripeEnumField(enum=enums.CardFundingType, help_text='Card funding type.') class-attribute
djstripe.models.payment_methods.Card.last4 = models.CharField(max_length=4, help_text='Last four digits of Card number.') class-attribute
djstripe.models.payment_methods.Card.name = models.TextField(max_length=5000, default='', blank=True, help_text='Cardholder name.') class-attribute
djstripe.models.payment_methods.Card.stripe_class = stripe.Card class-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

Functions

djstripe.models.payment_methods.Card.__str__()
Source code in djstripe/models/payment_methods.py
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
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 {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
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
@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.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
 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
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`",
    )
    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`",
    )
    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`",
    )
    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`"
        ),
    )
    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`",
    )
    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`",
    )
    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
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
djstripe.models.payment_methods.PaymentMethod.alipay = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `alipay`') class-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
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
djstripe.models.payment_methods.PaymentMethod.bancontact = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `bancontact`') class-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
djstripe.models.payment_methods.PaymentMethod.boleto = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `boleto`') class-attribute
djstripe.models.payment_methods.PaymentMethod.card = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `card`') class-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
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
djstripe.models.payment_methods.PaymentMethod.description = None class-attribute
djstripe.models.payment_methods.PaymentMethod.eps = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `eps`') class-attribute
djstripe.models.payment_methods.PaymentMethod.fpx = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `fpx`') class-attribute
djstripe.models.payment_methods.PaymentMethod.giropay = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `giropay`') class-attribute
djstripe.models.payment_methods.PaymentMethod.grabpay = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `grabpay`') class-attribute
djstripe.models.payment_methods.PaymentMethod.ideal = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `ideal`') class-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
djstripe.models.payment_methods.PaymentMethod.oxxo = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `oxxo`') class-attribute
djstripe.models.payment_methods.PaymentMethod.p24 = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `p24`') class-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
djstripe.models.payment_methods.PaymentMethod.sofort = JSONField(null=True, blank=True, help_text='Additional information for payment methods of type `sofort`') class-attribute
djstripe.models.payment_methods.PaymentMethod.stripe_class = stripe.PaymentMethod class-attribute
djstripe.models.payment_methods.PaymentMethod.type = StripeEnumField(enum=enums.PaymentMethodType, help_text='The type of the PaymentMethod.') class-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

Functions

djstripe.models.payment_methods.PaymentMethod.__str__()
Source code in djstripe/models/payment_methods.py
932
933
934
935
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_objects_hook(cls, data, api_key=djstripe_settings.STRIPE_SECRET_KEY, current_ids=None)
Source code in djstripe/models/payment_methods.py
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
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
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
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
@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
 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
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
937
938
939
940
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
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
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
        """
        return Customer.stripe_class.list_sources(
            object="source", api_key=api_key, **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
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
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
djstripe.models.payment_methods.Source.currency = StripeCurrencyCodeField(default='', blank=True) class-attribute
djstripe.models.payment_methods.Source.customer = StripeForeignKey('Customer', on_delete=models.SET_NULL, null=True, blank=True, related_name='sources') class-attribute
djstripe.models.payment_methods.Source.flow = StripeEnumField(enum=enums.SourceFlow, help_text='The authentication flow of the source.') class-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
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
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
djstripe.models.payment_methods.Source.source_data = JSONField(help_text='The data corresponding to the source type.') class-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
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
djstripe.models.payment_methods.Source.stripe_class = stripe.Source class-attribute
djstripe.models.payment_methods.Source.stripe_dashboard_item_name = 'sources' class-attribute
djstripe.models.payment_methods.Source.type = StripeEnumField(enum=enums.SourceType, help_text='The type of the source.') class-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

Functions

djstripe.models.payment_methods.Source.__str__()
Source code in djstripe/models/payment_methods.py
730
731
def __str__(self):
    return f"{self.type} {self.id}"
djstripe.models.payment_methods.Source._attach_objects_hook(cls, data, api_key=djstripe_settings.STRIPE_SECRET_KEY, current_ids=None)
Source code in djstripe/models/payment_methods.py
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
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
djstripe.models.payment_methods.Source._manipulate_stripe_object_hook(data) classmethod
Source code in djstripe/models/payment_methods.py
733
734
735
736
737
@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
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
780
781
782
783
784
785
786
787
788
789
790
791
792
@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
    """
    return Customer.stripe_class.list_sources(
        object="source", api_key=api_key, **kwargs
    ).auto_paging_iter()
djstripe.models.payment_methods.Source.detach()

Detach the source from its customer.

Source code in djstripe/models/payment_methods.py
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
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

Functions

Billing

Attributes

Classes

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
 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
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 {duration_in_months} month"
            else:
                duration = "for {duration_in_months} months"
            duration = duration.format(duration_in_months=self.duration_in_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
djstripe.models.billing.Coupon.applies_to = JSONField(null=True, blank=True, help_text='Contains information about what this coupon applies to.') class-attribute
djstripe.models.billing.Coupon.currency = StripeCurrencyCodeField(null=True, blank=True) class-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
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
djstripe.models.billing.Coupon.expand_fields = ['applies_to'] class-attribute
djstripe.models.billing.Coupon.id = StripeIdField(max_length=500) class-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
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
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
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
djstripe.models.billing.Coupon.stripe_class = stripe.Coupon class-attribute
djstripe.models.billing.Coupon.stripe_dashboard_item_name = 'coupons' class-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

Classes

djstripe.models.billing.Coupon.Meta

Bases: StripeModel.Meta

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

Functions

djstripe.models.billing.Coupon.__str__()
Source code in djstripe/models/billing.py
171
172
173
174
def __str__(self):
    if self.name:
        return self.name
    return self.human_readable
djstripe.models.billing.Coupon.human_readable() property
Source code in djstripe/models/billing.py
186
187
188
189
190
191
192
193
194
195
196
@property
def human_readable(self):
    if self.duration == enums.CouponDuration.repeating:
        if self.duration_in_months == 1:
            duration = "for {duration_in_months} month"
        else:
            duration = "for {duration_in_months} months"
        duration = duration.format(duration_in_months=self.duration_in_months)
    else:
        duration = self.duration
    return f"{self.human_readable_amount} {duration}"
djstripe.models.billing.Coupon.human_readable_amount() property
Source code in djstripe/models/billing.py
176
177
178
179
180
181
182
183
184
@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"

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
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
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
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

Functions

djstripe.models.billing.Invoice._attach_objects_post_save_hook(cls, data, api_key=djstripe_settings.STRIPE_SECRET_KEY, pending_relations=None)
Source code in djstripe/models/billing.py
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
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,
    )

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
 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
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

    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.",
    )
    # TODO: 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
                )
            )

    def __str__(self):
        return self.description

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

    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
djstripe.models.billing.InvoiceItem.currency = StripeCurrencyCodeField() class-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
djstripe.models.billing.InvoiceItem.date = StripeDateTimeField(help_text='The date on the invoiceitem.') class-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
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
djstripe.models.billing.InvoiceItem.period = JSONField() class-attribute
djstripe.models.billing.InvoiceItem.period_end = StripeDateTimeField(help_text="Might be the date when this invoiceitem's invoice was sent.") class-attribute
djstripe.models.billing.InvoiceItem.period_start = StripeDateTimeField(help_text='Might be the date when this invoiceitem was added to the invoice') class-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
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
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
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
djstripe.models.billing.InvoiceItem.stripe_class = stripe.InvoiceItem class-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
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
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
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

Functions

djstripe.models.billing.InvoiceItem.__str__()
Source code in djstripe/models/billing.py
1039
1040
def __str__(self):
    return self.description
djstripe.models.billing.InvoiceItem._attach_objects_post_save_hook(cls, data, api_key=djstripe_settings.STRIPE_SECRET_KEY, pending_relations=None)
Source code in djstripe/models/billing.py
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
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
            )
        )
djstripe.models.billing.InvoiceItem._manipulate_stripe_object_hook(data) classmethod
Source code in djstripe/models/billing.py
1013
1014
1015
1016
1017
1018
@classmethod
def _manipulate_stripe_object_hook(cls, data):
    data["period_start"] = data["period"]["start"]
    data["period_end"] = data["period"]["end"]

    return data
djstripe.models.billing.InvoiceItem.api_retrieve(*args, **kwargs)
Source code in djstripe/models/billing.py
1049
1050
1051
1052
1053
1054
1055
1056
1057
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
1046
1047
def get_stripe_dashboard_url(self):
    return self.invoice.get_stripe_dashboard_url()
djstripe.models.billing.InvoiceItem.is_valid_object(data) classmethod
Source code in djstripe/models/billing.py
1042
1043
1044
@classmethod
def is_valid_object(cls, data):
    return data and data.get("object") in ("invoiceitem", "line_item")

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
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
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 Plan.objects.get(id=kwargs["id"]), False
        except Plan.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
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
djstripe.models.billing.Plan.amount = StripeDecimalCurrencyAmountField(null=True, blank=True, help_text='Amount (as decimal) to be charged on the interval specified.') class-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
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
djstripe.models.billing.Plan.currency = StripeCurrencyCodeField() class-attribute
djstripe.models.billing.Plan.expand_fields = ['product', 'tiers'] class-attribute
djstripe.models.billing.Plan.interval = StripeEnumField(enum=enums.PlanInterval, help_text='The frequency with which a subscription should be billed.') class-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
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
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
djstripe.models.billing.Plan.stripe_class = stripe.Plan class-attribute
djstripe.models.billing.Plan.stripe_dashboard_item_name = 'plans' class-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
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
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
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
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

Classes

djstripe.models.billing.Plan.Meta

Bases: object

Source code in djstripe/models/billing.py
1194
1195
class Meta(object):
    ordering = ["amount"]
Attributes
djstripe.models.billing.Plan.Meta.ordering = ['amount'] class-attribute

Functions

djstripe.models.billing.Plan.__str__()
Source code in djstripe/models/billing.py
1221
1222
1223
1224
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.amount_in_cents() property
Source code in djstripe/models/billing.py
1226
1227
1228
@property
def amount_in_cents(self):
    return int(self.amount * 100)
djstripe.models.billing.Plan.create(**kwargs) classmethod
Source code in djstripe/models/billing.py
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
@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
1197
1198
1199
1200
1201
1202
1203
1204
@classmethod
def get_or_create(cls, **kwargs):
    """Get or create a Plan."""

    try:
        return Plan.objects.get(id=kwargs["id"]), False
    except Plan.DoesNotExist:
        return cls.create(**kwargs), True
djstripe.models.billing.Plan.human_readable_price() property
Source code in djstripe/models/billing.py
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
@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))

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
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
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
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
djstripe.models.billing.ShippingRate.description = None class-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
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
djstripe.models.billing.ShippingRate.stripe_class = stripe.ShippingRate class-attribute
djstripe.models.billing.ShippingRate.stripe_dashboard_item_name = 'shipping-rates' class-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
djstripe.models.billing.ShippingRate.tax_code = StripeForeignKey('TaxCode', null=True, blank=True, on_delete=models.CASCADE, help_text='The shipping tax code') class-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

Classes

djstripe.models.billing.ShippingRate.Meta

Bases: StripeModel.Meta

Source code in djstripe/models/billing.py
2075
2076
class Meta(StripeModel.Meta):
    verbose_name = "Shipping Rate"
Attributes
djstripe.models.billing.ShippingRate.Meta.verbose_name = 'Shipping Rate' class-attribute

Functions

djstripe.models.billing.ShippingRate.__str__()
Source code in djstripe/models/billing.py
2078
2079
2080
2081
2082
2083
2084
2085
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
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
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
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
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
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
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
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()

    def __str__(self):

        subscriptions_lst = self.customer._get_valid_subscriptions()
        products_lst = [
            subscription.plan.product.name
            for subscription in subscriptions_lst
            if subscription and subscription.plan and subscription.plan.product
        ]

        return f"{self.customer} on {' and '.join(products_lst)}"

    @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
        """
        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,
        prorate: bool = 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

        # In short: We used to have a `prorate` argument which defaulted to
        # a DJSTRIPE_PRORATION_POLICY setting.
        # This is overly complex and specific, so we are dropping support for this.
        # To override it, you can pass `proration_behavior`.
        # If instead you pass `prorate`, we will transform it until dj-stripe 2.8.
        # If you have DJSTRIPE_PRORATION_POLICY set, we will default to it for now.
        # In 2.8, we will ignore both of those and let Stripe figure it out.
        # Stripe's default proration policy is specified here:
        # https://stripe.com/docs/billing/subscriptions/prorations
        if "proration_behavior" not in kwargs:
            if prorate is not None:
                warnings.warn(
                    "The `prorate` parameter to Subscription.update() is deprecated "
                    "by Stripe. Use `proration_behavior` instead.\n"
                    "Read more: "
                    "https://stripe.com/docs/billing/subscriptions/prorations",
                    DeprecationWarning,
                )
            elif kwargs.get("subscription_prorate") is not None:
                warnings.warn(
                    "The `subscription_prorate` parameter to Subscription.update() is deprecated "
                    "by Stripe. Use `proration_behavior` instead.\n"
                    "Read more: "
                    "https://stripe.com/docs/billing/subscriptions/prorations",
                    DeprecationWarning,
                )

            else:
                prorate = djstripe_settings.PRORATION_POLICY
                if prorate is not None:
                    warnings.warn(
                        "The `DJSTRIPE_PRORATION_POLICY` setting is deprecated and will "
                        "be ignored in dj-stripe 2.8. "
                        "Specify `proration_behavior` instead."
                    )
                else:
                    prorate = False

            if prorate:
                kwargs.setdefault("proration_behavior", "create_prorations")
            else:
                kwargs.setdefault("proration_behavior", "none")
        elif prorate is not None:
            raise TypeError(
                "`prorate` argument must not be set when `proration_behavior` is specified"
            )

        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)
        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
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
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
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
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
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
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
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
djstripe.models.billing.Subscription.current_period_start = StripeDateTimeField(help_text='Start of the current period for which the subscription has been invoiced.') class-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
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
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
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
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
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
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
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
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
djstripe.models.billing.Subscription.objects = SubscriptionManager() class-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
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
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
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
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
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
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
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
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
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
djstripe.models.billing.Subscription.status = StripeEnumField(enum=enums.SubscriptionStatus, help_text='The status of this subscription.') class-attribute
djstripe.models.billing.Subscription.stripe_class = stripe.Subscription class-attribute
djstripe.models.billing.Subscription.stripe_dashboard_item_name = 'subscriptions' class-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
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

Functions

djstripe.models.billing.Subscription.__str__()
Source code in djstripe/models/billing.py
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
def __str__(self):

    subscriptions_lst = self.customer._get_valid_subscriptions()
    products_lst = [
        subscription.plan.product.name
        for subscription in subscriptions_lst
        if subscription and subscription.plan and subscription.plan.product
    ]

    return f"{self.customer} on {' and '.join(products_lst)}"
djstripe.models.billing.Subscription._attach_objects_post_save_hook(cls, data, api_key=djstripe_settings.STRIPE_SECRET_KEY, pending_relations=None)
Source code in djstripe/models/billing.py
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
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
        )
    )
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
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
@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
    """
    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
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
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)
    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
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
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
1721
1722
1723
1724
1725
1726
1727
1728
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
1730
1731
1732
1733
1734
1735
1736
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
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
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
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
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
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
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, prorate=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
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
def update(
    self,
    plan: Union[StripeModel, str] = None,
    prorate: bool = 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

    # In short: We used to have a `prorate` argument which defaulted to
    # a DJSTRIPE_PRORATION_POLICY setting.
    # This is overly complex and specific, so we are dropping support for this.
    # To override it, you can pass `proration_behavior`.
    # If instead you pass `prorate`, we will transform it until dj-stripe 2.8.
    # If you have DJSTRIPE_PRORATION_POLICY set, we will default to it for now.
    # In 2.8, we will ignore both of those and let Stripe figure it out.
    # Stripe's default proration policy is specified here:
    # https://stripe.com/docs/billing/subscriptions/prorations
    if "proration_behavior" not in kwargs:
        if prorate is not None:
            warnings.warn(
                "The `prorate` parameter to Subscription.update() is deprecated "
                "by Stripe. Use `proration_behavior` instead.\n"
                "Read more: "
                "https://stripe.com/docs/billing/subscriptions/prorations",
                DeprecationWarning,
            )
        elif kwargs.get("subscription_prorate") is not None:
            warnings.warn(
                "The `subscription_prorate` parameter to Subscription.update() is deprecated "
                "by Stripe. Use `proration_behavior` instead.\n"
                "Read more: "
                "https://stripe.com/docs/billing/subscriptions/prorations",
                DeprecationWarning,
            )

        else:
            prorate = djstripe_settings.PRORATION_POLICY
            if prorate is not None:
                warnings.warn(
                    "The `DJSTRIPE_PRORATION_POLICY` setting is deprecated and will "
                    "be ignored in dj-stripe 2.8. "
                    "Specify `proration_behavior` instead."
                )
            else:
                prorate = False

        if prorate:
            kwargs.setdefault("proration_behavior", "create_prorations")
        else:
            kwargs.setdefault("proration_behavior", "none")
    elif prorate is not None:
        raise TypeError(
            "`prorate` argument must not be set when `proration_behavior` is specified"
        )

    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
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
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
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
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
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
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
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
djstripe.models.billing.SubscriptionItem.stripe_class = stripe.SubscriptionItem class-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
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

Functions

djstripe.models.billing.SubscriptionItem._attach_objects_post_save_hook(cls, data, api_key=djstripe_settings.STRIPE_SECRET_KEY, pending_relations=None)
Source code in djstripe/models/billing.py
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
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
        )
    )

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
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
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, **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, **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
djstripe.models.billing.SubscriptionSchedule.completed_at = StripeDateTimeField(null=True, blank=True, help_text='Time at which the subscription schedule was completed.') class-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
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
djstripe.models.billing.SubscriptionSchedule.default_settings = JSONField(null=True, blank=True, help_text="Object representing the subscription schedule's default settings.") class-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
djstripe.models.billing.SubscriptionSchedule.phases = JSONField(null=True, blank=True, help_text="Configuration for the subscription schedule's phases.") class-attribute
djstripe.models.billing.SubscriptionSchedule.released_at = StripeDateTimeField(null=True, blank=True, help_text='Time at which the subscription schedule was released.') class-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
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
djstripe.models.billing.SubscriptionSchedule.stripe_class = stripe.SubscriptionSchedule class-attribute
djstripe.models.billing.SubscriptionSchedule.stripe_dashboard_item_name = 'subscription_schedules' class-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

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
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
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, **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
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
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, **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
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
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
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
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
djstripe.models.billing.TaxCode.name = models.CharField(max_length=128, help_text='A short name for the tax code.') class-attribute
djstripe.models.billing.TaxCode.stripe_class = stripe.TaxCode class-attribute

Classes

djstripe.models.billing.TaxCode.Meta

Bases: StripeModel.Meta

Source code in djstripe/models/billing.py
2103
2104
class Meta(StripeModel.Meta):
    verbose_name = "Tax Code"
Attributes
djstripe.models.billing.TaxCode.Meta.verbose_name = 'Tax Code' class-attribute

Functions

djstripe.models.billing.TaxCode.__str__()
Source code in djstripe/models/billing.py
2106
2107
def __str__(self):
    return f"{self.name}: {self.id}"
djstripe.models.billing.TaxCode._find_owner_account(data, api_key=djstripe_settings.STRIPE_SECRET_KEY) classmethod
Source code in djstripe/models/billing.py
2109
2110
2111
2112
@classmethod
def _find_owner_account(cls, data, api_key=djstripe_settings.STRIPE_SECRET_KEY):
    # Tax Codes do not belong to any Stripe Account
    pass

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
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
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

        # 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 or self.default_api_key,
            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
        """
        return stripe.Customer.list_tax_ids(
            api_key=api_key, **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
djstripe.models.billing.TaxId.customer = StripeForeignKey('djstripe.customer', on_delete=models.CASCADE, related_name='tax_ids') class-attribute
djstripe.models.billing.TaxId.description = None class-attribute
djstripe.models.billing.TaxId.metadata = None class-attribute
djstripe.models.billing.TaxId.stripe_class = stripe.TaxId class-attribute
djstripe.models.billing.TaxId.type = StripeEnumField(enum=enums.TaxIdType, help_text='The status of this subscription.') class-attribute
djstripe.models.billing.TaxId.value = models.CharField(max_length=50, help_text='Value of the tax ID.') class-attribute
djstripe.models.billing.TaxId.verification = JSONField(help_text='Tax ID verification information.') class-attribute

Classes

djstripe.models.billing.TaxId.Meta

Bases: StripeModel.Meta

Source code in djstripe/models/billing.py
2144
2145
class Meta(StripeModel.Meta):
    verbose_name = "Tax ID"
Attributes
djstripe.models.billing.TaxId.Meta.verbose_name = 'Tax ID' class-attribute

Functions

djstripe.models.billing.TaxId.__str__()
Source code in djstripe/models/billing.py
2141
2142
def __str__(self):
    return f"{enums.TaxIdType.humanize(self.type)} {self.value} ({self.verification.get('status')})"
djstripe.models.billing.TaxId._api_create(api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs) classmethod

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

Source code in djstripe/models/billing.py
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
@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)
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
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
@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
    """
    return stripe.Customer.list_tax_ids(
        api_key=api_key, **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
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
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

    # 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 or self.default_api_key,
        expand=self.expand_fields,
        stripe_account=stripe_account,
    )

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
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
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
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
djstripe.models.billing.TaxRate.country = models.CharField(max_length=2, default='', blank=True, help_text='Two-letter country code.') class-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
djstripe.models.billing.TaxRate.inclusive = models.BooleanField(help_text='This specifies if the tax rate is inclusive or exclusive.') class-attribute
djstripe.models.billing.TaxRate.jurisdiction = models.CharField(max_length=50, default='', blank=True, help_text='The jurisdiction for the tax rate.') class-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
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
djstripe.models.billing.TaxRate.stripe_class = stripe.TaxRate class-attribute
djstripe.models.billing.TaxRate.stripe_dashboard_item_name = 'tax-rates' class-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

Classes

djstripe.models.billing.TaxRate.Meta

Bases: StripeModel.Meta

Source code in djstripe/models/billing.py
2266
2267
class Meta(StripeModel.Meta):
    verbose_name = "Tax Rate"
Attributes
djstripe.models.billing.TaxRate.Meta.verbose_name = 'Tax Rate' class-attribute

Functions

djstripe.models.billing.TaxRate.__str__()
Source code in djstripe/models/billing.py
2263
2264
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
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
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._invoiceitems = []
        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._invoiceitems = cls._stripe_object_to_invoice_items(
            target_cls=InvoiceItem, 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.
        """

        return QuerySetMock.from_iterable(InvoiceItem, self._invoiceitems)

    @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_tax_rates = [] instance-attribute
djstripe.models.billing.UpcomingInvoice._invoiceitems = [] instance-attribute
djstripe.models.billing.UpcomingInvoice._total_tax_amounts = [] instance-attribute
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

Functions

djstripe.models.billing.UpcomingInvoice.__init__(*args, **kwargs)
Source code in djstripe/models/billing.py
810
811
812
813
814
def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self._invoiceitems = []
    self._default_tax_rates = []
    self._total_tax_amounts = []
djstripe.models.billing.UpcomingInvoice._attach_objects_hook(cls, data, api_key=djstripe_settings.STRIPE_SECRET_KEY, current_ids=None)
Source code in djstripe/models/billing.py
819
820
821
822
823
824
825
826
827
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._invoiceitems = cls._stripe_object_to_invoice_items(
        target_cls=InvoiceItem, data=data, invoice=self, api_key=api_key
    )
djstripe.models.billing.UpcomingInvoice._attach_objects_post_save_hook(cls, data, api_key=djstripe_settings.STRIPE_SECRET_KEY, pending_relations=None)
Source code in djstripe/models/billing.py
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
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
djstripe.models.billing.UpcomingInvoice.default_tax_rates() property

Gets the default tax rates associated with this upcoming invoice. :return:

Source code in djstripe/models/billing.py
880
881
882
883
884
885
886
@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)
djstripe.models.billing.UpcomingInvoice.get_stripe_dashboard_url()
Source code in djstripe/models/billing.py
816
817
def get_stripe_dashboard_url(self):
    return ""
djstripe.models.billing.UpcomingInvoice.id() writable property
Source code in djstripe/models/billing.py
898
899
900
@property
def id(self):
    return None
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.

Source code in djstripe/models/billing.py
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
@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.
    """

    return QuerySetMock.from_iterable(InvoiceItem, self._invoiceitems)
djstripe.models.billing.UpcomingInvoice.save(*args, **kwargs)
Source code in djstripe/models/billing.py
906
907
def save(self, *args, **kwargs):
    return  # noop
djstripe.models.billing.UpcomingInvoice.total_tax_amounts() property

Gets the total tax amounts associated with this upcoming invoice. :return:

Source code in djstripe/models/billing.py
888
889
890
891
892
893
894
895
896
@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
    )

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
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
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
djstripe.models.billing.UsageRecord.description = None class-attribute
djstripe.models.billing.UsageRecord.metadata = None class-attribute
djstripe.models.billing.UsageRecord.quantity = models.PositiveIntegerField(help_text='The quantity of the plan to which the customer should be subscribed.') class-attribute
djstripe.models.billing.UsageRecord.stripe_class = stripe.UsageRecord class-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
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

Functions

djstripe.models.billing.UsageRecord.__str__()
Source code in djstripe/models/billing.py
2307
2308
def __str__(self):
    return f"Usage for {self.subscription_item} ({self.action}) is {self.quantity}"
djstripe.models.billing.UsageRecord._api_create(api_key=djstripe_settings.STRIPE_SECRET_KEY, **kwargs) classmethod

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

Source code in djstripe/models/billing.py
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
@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
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
2338
2339
2340
2341
2342
2343
@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
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
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 {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
        """
        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, **kwargs
        ).auto_paging_iter()

Attributes

djstripe.models.billing.UsageRecordSummary.description = None class-attribute
djstripe.models.billing.UsageRecordSummary.invoice = StripeForeignKey('Invoice', null=True, blank=True, on_delete=models.CASCADE, related_name='usage_record_summaries') class-attribute
djstripe.models.billing.UsageRecordSummary.metadata = None class-attribute
djstripe.models.billing.UsageRecordSummary.period = JSONField(null=True, blank=True, help_text='Subscription Billing period for the SubscriptionItem') class-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
djstripe.models.billing.UsageRecordSummary.period_start = StripeDateTimeField(null=True, blank=True, help_text='Start of the Subscription Billing period for the SubscriptionItem') class-attribute
djstripe.models.billing.UsageRecordSummary.stripe_class = stripe.UsageRecordSummary class-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
djstripe.models.billing.UsageRecordSummary.total_usage = models.PositiveIntegerField(help_text='The quantity of the plan to which the customer should be subscribed.') class-attribute

Functions

djstripe.models.billing.UsageRecordSummary.__str__()
Source code in djstripe/models/billing.py
2396
2397
def __str__(self):
    return f"Usage Summary for {self.subscription_item} ({self.invoice}) is {self.total_usage}"
djstripe.models.billing.UsageRecordSummary._manipulate_stripe_object_hook(data) classmethod
Source code in djstripe/models/billing.py
2399
2400
2401
2402
2403
2404
@classmethod
def _manipulate_stripe_object_hook(cls, data):
    data["period_start"] = data["period"]["start"]
    data["period_end"] = data["period"]["end"]

    return data
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
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
@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
    """
    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, **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
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)

        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, **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 {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.business_profile = JSONField(null=True, blank=True, help_text='Optional information related to the business.') class-attribute
djstripe.models.account.Account.business_type = StripeEnumField(enum=enums.BusinessType, default='', blank=True, help_text='The business type.') class-attribute
djstripe.models.account.Account.charges_enabled = models.BooleanField(help_text='Whether the account can create live charges') class-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
djstripe.models.account.Account.country = models.CharField(max_length=2, help_text='The country of the account') class-attribute
djstripe.models.account.Account.default_currency = StripeCurrencyCodeField(help_text='The currency this account has chosen to use as the default') class-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
djstripe.models.account.Account.email = models.CharField(max_length=255, help_text="The primary user's email address.") class-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
djstripe.models.account.Account.payouts_enabled = models.BooleanField(null=True, help_text='Whether Stripe can send payouts to this account') class-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
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
djstripe.models.account.Account.settings = JSONField(null=True, blank=True, help_text='Account options for customizing how the account functions within Stripe.') class-attribute
djstripe.models.account.Account.stripe_class = stripe.Account class-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
djstripe.models.account.Account.type = StripeEnumField(enum=enums.AccountType, help_text='The Stripe account type.') class-attribute

Functions

djstripe.models.account.Account.__str__()
Source code in djstripe/models/account.py
147
148
149
150
151
152
153
154
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
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, **kwargs
    )
djstripe.models.account.Account.branding_icon() property
Source code in djstripe/models/account.py
205
206
207
208
209
210
@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
Source code in djstripe/models/account.py
212
213
214
215
216
217
@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
djstripe.models.account.Account.business_url() property

The business's publicly available website.

Source code in djstripe/models/account.py
118
119
120
121
122
123
124
125
@property
def business_url(self) -> str:
    """
    The business's publicly available website.
    """
    if self.business_profile:
        return self.business_profile.get("url", "")
    return ""
djstripe.models.account.Account.default_api_key() property
Source code in djstripe/models/account.py
 99
100
101
@property
def default_api_key(self) -> str:
    return self.get_default_api_key()
djstripe.models.account.Account.get_default_account(api_key=djstripe_settings.STRIPE_SECRET_KEY) classmethod
Source code in djstripe/models/account.py
127
128
129
130
131
132
133
134
135
136
@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)

    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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
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
138
139
140
141
142
143
144
145
@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
92
93
94
95
96
97
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
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
djstripe.models.connect.ApplicationFee.amount = StripeQuantumCurrencyAmountField(help_text='Amount earned, in cents.') class-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
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
djstripe.models.connect.ApplicationFee.charge = StripeForeignKey('Charge', on_delete=models.CASCADE, help_text='The charge that the application fee was taken from.') class-attribute
djstripe.models.connect.ApplicationFee.currency = StripeCurrencyCodeField() class-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
djstripe.models.connect.ApplicationFee.stripe_class = stripe.ApplicationFee class-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
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
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
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
djstripe.models.connect.ApplicationFeeRefund.currency = StripeCurrencyCodeField() class-attribute
djstripe.models.connect.ApplicationFeeRefund.description = None class-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
djstripe.models.connect.ApplicationFeeRefund.stripe_class = stripe.ApplicationFeeRefund class-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
 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
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_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
djstripe.models.connect.CountrySpec.id = models.CharField(max_length=2, primary_key=True, serialize=True) class-attribute
djstripe.models.connect.CountrySpec.stripe_class = stripe.CountrySpec class-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
djstripe.models.connect.CountrySpec.supported_payment_currencies = JSONField(help_text='Currencies that can be accepted in the specified country (for payments).') class-attribute
djstripe.models.connect.CountrySpec.supported_payment_methods = JSONField(help_text='Payment methods available in the specified country.') class-attribute
djstripe.models.connect.CountrySpec.supported_transfer_countries = JSONField(help_text='Countries that can accept transfers from the specified country.') class-attribute
djstripe.models.connect.CountrySpec.verification_fields = JSONField(help_text='Lists the types of verification data needed to keep an account open.') class-attribute

Functions

djstripe.models.connect.CountrySpec.api_retrieve(api_key=None, stripe_account=None)
Source code in djstripe/models/connect.py
163
164
165
166
167
168
169
170
171
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_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
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
@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
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
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
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
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
djstripe.models.connect.Transfer.currency = StripeCurrencyCodeField() class-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
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
djstripe.models.connect.Transfer.expand_fields = ['balance_transaction'] class-attribute
djstripe.models.connect.Transfer.objects = TransferManager() class-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
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
djstripe.models.connect.Transfer.source_type = StripeEnumField(enum=enums.LegacySourceType, help_text='The source balance from which this transfer came.') class-attribute
djstripe.models.connect.Transfer.stripe_class = stripe.Transfer class-attribute
djstripe.models.connect.Transfer.stripe_dashboard_item_name = 'transfers' class-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

Functions

djstripe.models.connect.Transfer.__str__()
Source code in djstripe/models/connect.py
245
246
247
248
249
250
251
252
253
254
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.fee() property
Source code in djstripe/models/connect.py
240
241
242
243
@property
def fee(self):
    if self.balance_transaction:
        return self.balance_transaction.fee
djstripe.models.connect.Transfer.get_stripe_dashboard_url()
Source code in djstripe/models/connect.py
276
277
278
279
280
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
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
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, **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

        # 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 or self.default_api_key,
            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
        """
        return stripe.Transfer.list_reversals(
            api_key=api_key, **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
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
djstripe.models.connect.TransferReversal.currency = StripeCurrencyCodeField() class-attribute
djstripe.models.connect.TransferReversal.expand_fields = ['balance_transaction', 'transfer'] class-attribute
djstripe.models.connect.TransferReversal.stripe_class = stripe.Transfer class-attribute
djstripe.models.connect.TransferReversal.stripe_dashboard_item_name = 'transfer_reversals' class-attribute
djstripe.models.connect.TransferReversal.transfer = StripeForeignKey('Transfer', on_delete=models.CASCADE, help_text='The transfer that was reversed.', related_name='reversals') class-attribute

Functions

djstripe.models.connect.TransferReversal.__str__()
Source code in djstripe/models/connect.py
314
315
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
361
362
363
364
365
366
367
368
369
370
371
372
373
@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
    """
    return stripe.Transfer.list_reversals(
        api_key=api_key, **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
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
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

    # 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 or self.default_api_key,
        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
375
376
377
378
379
380
@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

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
 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
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", "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 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, **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, **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, **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
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
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
djstripe.models.orders.Order.automatic_tax = JSONField(help_text='Settings and latest results for automatic tax lookup for this Order.') class-attribute
djstripe.models.orders.Order.billing_details = JSONField(null=True, blank=True, help_text='Customer billing details associated with the order.') class-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
djstripe.models.orders.Order.currency = StripeCurrencyCodeField(help_text='Three-letter ISO currency code, in lowercase. Must be a supported currency.') class-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
djstripe.models.orders.Order.discounts = JSONField(null=True, blank=True, help_text='The discounts applied to the order.') class-attribute
djstripe.models.orders.Order.expand_fields = ['customer', 'line_items', 'total_details.breakdown'] class-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
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
djstripe.models.orders.Order.payment = JSONField(help_text='Payment information associated with the order. Includes payment status, settings, and a PaymentIntent ID') class-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
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
djstripe.models.orders.Order.shipping_details = JSONField(null=True, blank=True, help_text='Customer shipping information associated with the order.') class-attribute
djstripe.models.orders.Order.status = StripeEnumField(enum=OrderStatus, help_text='The overall status of the order.') class-attribute
djstripe.models.orders.Order.stripe_class = stripe.Order class-attribute
djstripe.models.orders.Order.stripe_dashboard_item_name = 'orders' class-attribute
djstripe.models.orders.Order.tax_details = JSONField(null=True, blank=True, help_text='Tax details about the purchaser for this order.') class-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

Functions

djstripe.models.orders.Order.__str__()
Source code in djstripe/models/orders.py
110
111
112
113
114
115
116
117
118
119
120
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
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, **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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
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, **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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
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, **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
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
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
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
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
djstripe.models.sigma.ScheduledQueryRun.sql = models.TextField(max_length=5000, help_text='SQL for the query.') class-attribute
djstripe.models.sigma.ScheduledQueryRun.status = StripeEnumField(enum=enums.ScheduledQueryRunStatus, help_text="The query's execution status.") class-attribute
djstripe.models.sigma.ScheduledQueryRun.stripe_class = stripe.sigma.ScheduledQueryRun class-attribute
djstripe.models.sigma.ScheduledQueryRun.title = models.TextField(max_length=5000, help_text='Title of the query.') class-attribute

Functions

djstripe.models.sigma.ScheduledQueryRun.__str__()
Source code in djstripe/models/sigma.py
45
46
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
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",
    )

    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")

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
djstripe.models.webhooks.WebhookEndpoint.application = models.CharField(max_length=255, blank=True, help_text='The ID of the associated Connect application.') class-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
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
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
djstripe.models.webhooks.WebhookEndpoint.status = StripeEnumField(enum=WebhookEndpointStatus, help_text='The status of the webhook. It can be enabled or disabled.') class-attribute
djstripe.models.webhooks.WebhookEndpoint.stripe_class = stripe.WebhookEndpoint class-attribute
djstripe.models.webhooks.WebhookEndpoint.stripe_dashboard_item_name = 'webhooks' class-attribute
djstripe.models.webhooks.WebhookEndpoint.url = models.URLField(help_text='The URL of the webhook endpoint.', max_length=2048) class-attribute

Functions

djstripe.models.webhooks.WebhookEndpoint.__str__()
Source code in djstripe/models/webhooks.py
64
65
def __str__(self):
    return self.url or str(self.djstripe_uuid)

djstripe.models.webhooks.WebhookEventTrigger

Bases: 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.

Source code in djstripe/models/webhooks.py
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
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:
            obj.valid = obj.validate(secret=secret, api_key=api_key)
            if obj.valid:
                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)
        except Exception as e:
            max_length = WebhookEventTrigger._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
            webhook_processing_error.send(
                sender=WebhookEventTrigger,
                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
    ):
        pass

    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: {}".format(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 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")
            local_cli_signing_secret = headers.get("x-djstripe-webhook-secret")
            try:
                # check if the x-djstripe-webhook-secret Custom Header exists
                if local_cli_signing_secret:
                    # Set Endpoint Signing Secret to the output of Stripe CLI
                    # for signature verification
                    secret = local_cli_signing_secret

                stripe.WebhookSignature.verify_header(
                    self.body, signature, secret, tolerance
                )
            except stripe.error.SignatureVerificationError:
                logger.exception("Failed to verify header")
                return False
            else:
                return True

        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
        with stripe_temporary_api_version(local_data["api_version"], validate=False):
            remote_data = Event.stripe_class.retrieve(
                id=local_data["id"], api_key=api_key
            )

        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
djstripe.models.webhooks.WebhookEventTrigger.created = models.DateTimeField(auto_now_add=True) class-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
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
djstripe.models.webhooks.WebhookEventTrigger.exception = models.CharField(max_length=128, blank=True) class-attribute
djstripe.models.webhooks.WebhookEventTrigger.headers = JSONField() class-attribute
djstripe.models.webhooks.WebhookEventTrigger.id = models.BigAutoField(primary_key=True) class-attribute
djstripe.models.webhooks.WebhookEventTrigger.processed = models.BooleanField(default=False, help_text='Whether or not the webhook event has been successfully processed') class-attribute
djstripe.models.webhooks.WebhookEventTrigger.remote_ip = models.GenericIPAddressField(help_text='IP address of the request client.') class-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
djstripe.models.webhooks.WebhookEventTrigger.traceback = models.TextField(blank=True, help_text='Traceback if an exception was thrown during processing') class-attribute
djstripe.models.webhooks.WebhookEventTrigger.updated = models.DateTimeField(auto_now=True) class-attribute
djstripe.models.webhooks.WebhookEventTrigger.valid = models.BooleanField(default=False, help_text='Whether or not the webhook event has passed validation') class-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

Functions

djstripe.models.webhooks.WebhookEventTrigger.__str__()
Source code in djstripe/models/webhooks.py
171
172
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
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
@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:
        obj.valid = obj.validate(secret=secret, api_key=api_key)
        if obj.valid:
            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)
    except Exception as e:
        max_length = WebhookEventTrigger._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
        webhook_processing_error.send(
            sender=WebhookEventTrigger,
            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.is_test_event() property
Source code in djstripe/models/webhooks.py
252
253
254
255
@property
def is_test_event(self):
    event_id = self.json_body.get("id")
    return event_id and event_id.endswith("_00000000000000")
djstripe.models.webhooks.WebhookEventTrigger.json_body()
Source code in djstripe/models/webhooks.py
245
246
247
248
249
250
@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
326
327
328
329
330
331
332
333
334
335
336
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
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
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: {}".format(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 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")
        local_cli_signing_secret = headers.get("x-djstripe-webhook-secret")
        try:
            # check if the x-djstripe-webhook-secret Custom Header exists
            if local_cli_signing_secret:
                # Set Endpoint Signing Secret to the output of Stripe CLI
                # for signature verification
                secret = local_cli_signing_secret

            stripe.WebhookSignature.verify_header(
                self.body, signature, secret, tolerance
            )
        except stripe.error.SignatureVerificationError:
            logger.exception("Failed to verify header")
            return False
        else:
            return True

    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
    with stripe_temporary_api_version(local_data["api_version"], validate=False):
        remote_data = Event.stripe_class.retrieve(
            id=local_data["id"], api_key=api_key
        )

    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
257
258
259
260
def verify_signature(
    self, secret: str, tolerance: int = djstripe_settings.WEBHOOK_TOLERANCE
):
    pass

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
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
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
    """

    # HTTP_X_FORWARDED_FOR is relevant for django running behind a proxy
    x_forwarded_for = request.META.get("HTTP_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