canvas 0.53.2__py3-none-any.whl → 0.55.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of canvas might be problematic. Click here for more details.

@@ -856,6 +856,7 @@ class EventType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
856
856
  PATIENT_CHART_SUMMARY__SECTION_CONFIGURATION: _ClassVar[EventType]
857
857
  PATIENT_PROFILE__SECTION_CONFIGURATION: _ClassVar[EventType]
858
858
  PATIENT_PROFILE__ADD_PHARMACY__POST_SEARCH: _ClassVar[EventType]
859
+ PATIENT_CHART__MEDICATIONS: _ClassVar[EventType]
859
860
  CLAIM__CONDITIONS: _ClassVar[EventType]
860
861
  PLUGIN_CREATED: _ClassVar[EventType]
861
862
  PLUGIN_UPDATED: _ClassVar[EventType]
@@ -899,6 +900,13 @@ class EventType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
899
900
  DOCUMENT_REFERENCE_CREATED: _ClassVar[EventType]
900
901
  DOCUMENT_REFERENCE_UPDATED: _ClassVar[EventType]
901
902
  DOCUMENT_REFERENCE_DELETED: _ClassVar[EventType]
903
+ PANEL_SECTIONS_CONFIGURATION: _ClassVar[EventType]
904
+ REVENUE__PAYMENT_PROCESSOR__LIST: _ClassVar[EventType]
905
+ REVENUE__PAYMENT_PROCESSOR__CHARGE: _ClassVar[EventType]
906
+ REVENUE__PAYMENT_PROCESSOR__SELECTED: _ClassVar[EventType]
907
+ REVENUE__PAYMENT_PROCESSOR__PAYMENT_METHODS__LIST: _ClassVar[EventType]
908
+ REVENUE__PAYMENT_PROCESSOR__PAYMENT_METHODS__ADD: _ClassVar[EventType]
909
+ REVENUE__PAYMENT_PROCESSOR__PAYMENT_METHODS__REMOVE: _ClassVar[EventType]
902
910
  UNKNOWN: EventType
903
911
  ALLERGY_INTOLERANCE_CREATED: EventType
904
912
  ALLERGY_INTOLERANCE_UPDATED: EventType
@@ -1746,6 +1754,7 @@ PATIENT_CHART__CONDITIONS: EventType
1746
1754
  PATIENT_CHART_SUMMARY__SECTION_CONFIGURATION: EventType
1747
1755
  PATIENT_PROFILE__SECTION_CONFIGURATION: EventType
1748
1756
  PATIENT_PROFILE__ADD_PHARMACY__POST_SEARCH: EventType
1757
+ PATIENT_CHART__MEDICATIONS: EventType
1749
1758
  CLAIM__CONDITIONS: EventType
1750
1759
  PLUGIN_CREATED: EventType
1751
1760
  PLUGIN_UPDATED: EventType
@@ -1789,6 +1798,13 @@ PATIENT_METADATA_UPDATED: EventType
1789
1798
  DOCUMENT_REFERENCE_CREATED: EventType
1790
1799
  DOCUMENT_REFERENCE_UPDATED: EventType
1791
1800
  DOCUMENT_REFERENCE_DELETED: EventType
1801
+ PANEL_SECTIONS_CONFIGURATION: EventType
1802
+ REVENUE__PAYMENT_PROCESSOR__LIST: EventType
1803
+ REVENUE__PAYMENT_PROCESSOR__CHARGE: EventType
1804
+ REVENUE__PAYMENT_PROCESSOR__SELECTED: EventType
1805
+ REVENUE__PAYMENT_PROCESSOR__PAYMENT_METHODS__LIST: EventType
1806
+ REVENUE__PAYMENT_PROCESSOR__PAYMENT_METHODS__ADD: EventType
1807
+ REVENUE__PAYMENT_PROCESSOR__PAYMENT_METHODS__REMOVE: EventType
1792
1808
 
1793
1809
  class Event(_message.Message):
1794
1810
  __slots__ = ("type", "target", "context", "target_type")
@@ -8,7 +8,9 @@ caches: dict[tuple[str, str], Cache] = {}
8
8
 
9
9
 
10
10
  def get_cache(
11
- driver: str = "default", prefix: str = "", max_timeout_seconds: int | None = None
11
+ driver: str = "default",
12
+ prefix: str = "",
13
+ max_timeout_seconds: int | None = None,
12
14
  ) -> Cache:
13
15
  """Get the cache client based on the specified driver."""
14
16
  try:
@@ -3,18 +3,21 @@ from __future__ import annotations
3
3
  from typing import TYPE_CHECKING, Any
4
4
 
5
5
  from canvas_sdk.caching.client import get_cache as get_cache_client
6
- from canvas_sdk.utils.plugins import plugin_only
6
+ from canvas_sdk.utils.plugins import plugin_context
7
7
  from settings import CANVAS_SDK_CACHE_TIMEOUT_SECONDS
8
8
 
9
9
  if TYPE_CHECKING:
10
10
  from canvas_sdk.caching.base import Cache
11
11
 
12
12
 
13
- @plugin_only
13
+ @plugin_context
14
14
  def get_cache(**kwargs: Any) -> Cache:
15
15
  """Get the cache client for plugins."""
16
- prefix = kwargs["plugin_name"]
17
- return get_cache_client("plugins", prefix, CANVAS_SDK_CACHE_TIMEOUT_SECONDS)
16
+ return get_cache_client(
17
+ driver="plugins",
18
+ prefix=kwargs["plugin_name"],
19
+ max_timeout_seconds=CANVAS_SDK_CACHE_TIMEOUT_SECONDS,
20
+ )
18
21
 
19
22
 
20
23
  __exports__ = ("get_cache",)
@@ -0,0 +1,20 @@
1
+ from typing import Any
2
+
3
+ from canvas_sdk.base import Model
4
+
5
+
6
+ class Group(Model):
7
+ """
8
+ Class representing a group of items.
9
+ """
10
+
11
+ items: list[Any]
12
+ priority: int
13
+ name: str
14
+
15
+ def to_dict(self) -> dict[str, Any]:
16
+ """Convert the Group object to a dictionary."""
17
+ return {"items": self.items, "priority": self.priority, "name": self.name}
18
+
19
+
20
+ __exports__ = ("Group",)
@@ -0,0 +1,90 @@
1
+ from enum import Enum
2
+ from typing import Any
3
+
4
+ from pydantic import Field
5
+ from pydantic_core import InitErrorDetails
6
+
7
+ from canvas_sdk.effects.base import EffectType, _BaseEffect
8
+
9
+
10
+ class PanelConfiguration(_BaseEffect):
11
+ """
12
+ An Effect that will decide which buttons appear on the panel section in Canvas.
13
+ """
14
+
15
+ class Meta:
16
+ effect_type = EffectType.SHOW_PANEL_SECTIONS
17
+
18
+ class Page(Enum):
19
+ GLOBAL = "global"
20
+ PATIENT = "patient"
21
+
22
+ class PanelGlobalSection(Enum):
23
+ APPOINTMENT = "appointment"
24
+ CHANGE_REQUEST = "changeRequest"
25
+ IMAGING_REPORT = "imagingReport"
26
+ INPATIENT_STAY = "inpatientStay"
27
+ LAB_REPORT = "labReport"
28
+ MESSAGE = "message"
29
+ OUTSTANDING_REFERRAL = "outstandingReferral"
30
+ PRESCRIPTION_ALERT = "prescriptionAlert"
31
+ RECALL_APPOINTMENT = "recallAppointment"
32
+ REFERRAL_REPORT = "referralReport"
33
+ REFILL_REQUEST = "refillRequest"
34
+ TASK = "task"
35
+ UNCATEGORIZED_DOCUMENT = "uncategorizedDocument"
36
+
37
+ class PanelPatientSection(Enum):
38
+ CHANGE_REQUEST = "changeRequest"
39
+ COMMAND = "command"
40
+ IMAGING_REPORT = "imagingReport"
41
+ INPATIENT_STAY = "inpatientStay"
42
+ LAB_REPORT = "labReport"
43
+ PRESCRIPTION_ALERT = "prescriptionAlert"
44
+ REFERRAL_REPORT = "referralReport"
45
+ REFILL_REQUEST = "refillRequest"
46
+ TASK = "task"
47
+ UNCATEGORIZED_DOCUMENT = "uncategorizedDocument"
48
+
49
+ sections: list[PanelPatientSection] | list[PanelGlobalSection] = Field(min_length=1)
50
+ page: Page
51
+
52
+ @property
53
+ def values(self) -> dict[str, Any]:
54
+ """The PanelConfiguration's values."""
55
+ return {"sections": [s.value for s in self.sections]}
56
+
57
+ @property
58
+ def effect_payload(self) -> dict[str, Any]:
59
+ """The payload of the effect."""
60
+ return {"data": self.values}
61
+
62
+ def _get_error_details(self, method: Any) -> list[InitErrorDetails]:
63
+ errors = super()._get_error_details(method)
64
+
65
+ if self.page == PanelConfiguration.Page.GLOBAL and not all(
66
+ isinstance(s, PanelConfiguration.PanelGlobalSection) for s in self.sections
67
+ ):
68
+ errors.append(
69
+ self._create_error_detail(
70
+ "value",
71
+ "All sections must be of type PanelGlobalSection",
72
+ self.sections,
73
+ )
74
+ )
75
+
76
+ if self.page == PanelConfiguration.Page.PATIENT and not all(
77
+ isinstance(s, PanelConfiguration.PanelPatientSection) for s in self.sections
78
+ ):
79
+ errors.append(
80
+ self._create_error_detail(
81
+ "value",
82
+ "All sections must be of type PanelPatientSection",
83
+ self.sections,
84
+ )
85
+ )
86
+
87
+ return errors
88
+
89
+
90
+ __exports__ = ("PanelConfiguration",)
@@ -0,0 +1,30 @@
1
+ from typing import Any
2
+
3
+ from canvas_sdk.effects.base import EffectType, _BaseEffect
4
+ from canvas_sdk.effects.group import Group
5
+
6
+
7
+ class PatientChartGroup(_BaseEffect):
8
+ """
9
+ An Effect that groups chart items by name and priority.
10
+ """
11
+
12
+ class Meta:
13
+ effect_type = EffectType.PATIENT_CHART__GROUP_ITEMS
14
+
15
+ items: dict[str, Group]
16
+
17
+ @property
18
+ def values(self) -> dict[str, Any]:
19
+ """The chart items."""
20
+ return {
21
+ "items": [item.to_dict() for item in self.items.values() if isinstance(item, Group)]
22
+ }
23
+
24
+ @property
25
+ def effect_payload(self) -> dict[str, Any]:
26
+ """The payload of the effect."""
27
+ return {"data": self.values}
28
+
29
+
30
+ __exports__ = ("PatientChartGroup",)
@@ -0,0 +1,137 @@
1
+ from enum import StrEnum
2
+ from typing import Any
3
+
4
+ from canvas_sdk.effects import EffectType, _BaseEffect
5
+
6
+
7
+ class PaymentProcessorMetadata(_BaseEffect):
8
+ """PaymentProcessorInfo effect class."""
9
+
10
+ class Meta:
11
+ effect_type = EffectType.REVENUE__PAYMENT_PROCESSOR__METADATA
12
+
13
+ class PaymentProcessorType(StrEnum):
14
+ """Enum for payment processor types."""
15
+
16
+ CARD = "card"
17
+
18
+ identifier: str
19
+ type: PaymentProcessorType
20
+
21
+ @property
22
+ def values(self) -> dict[str, Any]:
23
+ """Return the values of the PaymentProcessorMetadata."""
24
+ return {
25
+ "identifier": self.identifier,
26
+ "type": self.type.value,
27
+ }
28
+
29
+
30
+ class PaymentProcessorForm(_BaseEffect):
31
+ """PaymentProcessorForm effect class."""
32
+
33
+ class Meta:
34
+ effect_type = EffectType.REVENUE__PAYMENT_PROCESSOR__FORM
35
+
36
+ intent: str
37
+ content: str
38
+
39
+ @property
40
+ def values(self) -> dict[str, Any]:
41
+ """Return the values of the PaymentProcessorMetadata."""
42
+ return {
43
+ "intent": self.intent,
44
+ "content": self.content,
45
+ }
46
+
47
+
48
+ class CardTransaction(_BaseEffect):
49
+ """CardTransaction effect class."""
50
+
51
+ class Meta:
52
+ effect_type = EffectType.REVENUE__PAYMENT_PROCESSOR__CREDIT_CARD_TRANSACTION
53
+
54
+ success: bool
55
+ transaction_id: str | None
56
+ error_code: str | None = None
57
+ api_response: dict
58
+
59
+ @property
60
+ def values(self) -> dict[str, Any]:
61
+ """Return the values of the CreditCardTransaction."""
62
+ return {
63
+ "success": self.success,
64
+ "transaction_id": self.transaction_id,
65
+ "api_response": self.api_response,
66
+ "error_code": self.error_code,
67
+ }
68
+
69
+
70
+ class PaymentMethod(_BaseEffect):
71
+ """PaymentMethod effect class."""
72
+
73
+ class Meta:
74
+ effect_type = EffectType.REVENUE__PAYMENT_PROCESSOR__PAYMENT_METHOD
75
+
76
+ payment_method_id: str
77
+ card_holder_name: str | None
78
+ brand: str
79
+ postal_code: str | None = None
80
+ country: str | None = None
81
+ expiration_year: int
82
+ expiration_month: int
83
+ card_last_four_digits: str
84
+
85
+ @property
86
+ def values(self) -> dict[str, Any]:
87
+ """Return the values of the PaymentMethod."""
88
+ return {
89
+ "payment_method_id": self.payment_method_id,
90
+ "card_holder_name": self.card_holder_name,
91
+ "brand": self.brand,
92
+ "postal_code": self.postal_code,
93
+ "country": self.country,
94
+ "expiration_year": self.expiration_year,
95
+ "expiration_month": self.expiration_month,
96
+ "card_last_four_digits": self.card_last_four_digits,
97
+ }
98
+
99
+
100
+ class AddPaymentMethodResponse(_BaseEffect):
101
+ """AddPaymentMethodResponse effect class."""
102
+
103
+ class Meta:
104
+ effect_type = EffectType.REVENUE__PAYMENT_PROCESSOR__PAYMENT_METHOD__ADD_RESPONSE
105
+
106
+ success: bool
107
+
108
+ @property
109
+ def values(self) -> dict[str, Any]:
110
+ """Return the values of the AddPaymentMethodResponse."""
111
+ return {"success": self.success}
112
+
113
+
114
+ class RemovePaymentMethodResponse(_BaseEffect):
115
+ """RemovePaymentMethodResponse effect class."""
116
+
117
+ class Meta:
118
+ effect_type = EffectType.REVENUE__PAYMENT_PROCESSOR__PAYMENT_METHOD__REMOVE_RESPONSE
119
+
120
+ success: bool
121
+
122
+ @property
123
+ def values(self) -> dict[str, Any]:
124
+ """Return the values of the RemovePaymentMethodResponse."""
125
+ return {
126
+ "success": self.success,
127
+ }
128
+
129
+
130
+ __exports__ = (
131
+ "PaymentProcessorMetadata",
132
+ "PaymentProcessorForm",
133
+ "CardTransaction",
134
+ "PaymentMethod",
135
+ "AddPaymentMethodResponse",
136
+ "RemovePaymentMethodResponse",
137
+ )
@@ -0,0 +1 @@
1
+ __exports__ = ()
@@ -0,0 +1,93 @@
1
+ import base64
2
+ from abc import ABC
3
+
4
+ from canvas_sdk.effects import Effect
5
+ from canvas_sdk.effects.payment_processor import PaymentProcessorMetadata
6
+ from canvas_sdk.events import EventType
7
+ from canvas_sdk.handlers import BaseHandler
8
+
9
+
10
+ class PaymentProcessor(BaseHandler, ABC):
11
+ """
12
+ Abstract Base class for payment processors.
13
+ """
14
+
15
+ RESPONDS_TO = [
16
+ EventType.Name(EventType.REVENUE__PAYMENT_PROCESSOR__LIST),
17
+ EventType.Name(EventType.REVENUE__PAYMENT_PROCESSOR__SELECTED),
18
+ EventType.Name(EventType.REVENUE__PAYMENT_PROCESSOR__CHARGE),
19
+ EventType.Name(EventType.REVENUE__PAYMENT_PROCESSOR__PAYMENT_METHODS__LIST),
20
+ EventType.Name(EventType.REVENUE__PAYMENT_PROCESSOR__PAYMENT_METHODS__ADD),
21
+ EventType.Name(EventType.REVENUE__PAYMENT_PROCESSOR__PAYMENT_METHODS__REMOVE),
22
+ ]
23
+
24
+ TYPE: PaymentProcessorMetadata.PaymentProcessorType
25
+
26
+ @property
27
+ def identifier(self) -> str:
28
+ """The application identifier."""
29
+ identifier = f"{self.__class__.__module__}:{self.__class__.__qualname__}"
30
+
31
+ return base64.b64encode(identifier.encode("utf-8")).decode("utf-8")
32
+
33
+ def compute(self) -> list[Effect]:
34
+ """Compute the effects to be applied."""
35
+ if self.event.type == EventType.REVENUE__PAYMENT_PROCESSOR__LIST and (
36
+ "payment_type" not in self.event.context
37
+ or self.event.context["payment_type"] == self.TYPE
38
+ ):
39
+ return [self.metadata().apply()]
40
+ elif self.event.type == EventType.REVENUE__PAYMENT_PROCESSOR__SELECTED:
41
+ effects = self._on_payment_processor_selected()
42
+ return effects
43
+ elif self.event.type == EventType.REVENUE__PAYMENT_PROCESSOR__CHARGE:
44
+ effect = self._charge()
45
+ return [effect] if effect else []
46
+ elif self.event.type == EventType.REVENUE__PAYMENT_PROCESSOR__PAYMENT_METHODS__LIST:
47
+ # This event is used to list payment methods, which may not be applicable for all processors.
48
+ # Subclasses should override this method if they support listing payment methods.
49
+ return self._payment_methods()
50
+ elif self.event.type == EventType.REVENUE__PAYMENT_PROCESSOR__PAYMENT_METHODS__ADD:
51
+ effect = self._add_payment_method()
52
+ return [effect] if effect else []
53
+ elif self.event.type == EventType.REVENUE__PAYMENT_PROCESSOR__PAYMENT_METHODS__REMOVE:
54
+ effect = self._remove_payment_method()
55
+ return [effect] if effect else []
56
+
57
+ return []
58
+
59
+ def metadata(self) -> PaymentProcessorMetadata:
60
+ """Return information about the payment processor."""
61
+ return PaymentProcessorMetadata(identifier=self.identifier, type=self.TYPE)
62
+
63
+ def _on_payment_processor_selected(self) -> list[Effect]:
64
+ """Handle the event when a payment processor is selected."""
65
+ # This method should be overridden by subclasses to handle specific logic
66
+ # when a payment processor is selected.
67
+ return []
68
+
69
+ def _charge(self) -> Effect | None:
70
+ """Handle the event when a charge is made."""
71
+ # This method should be overridden by subclasses to handle specific logic
72
+ # when a charge is made.
73
+ return None
74
+
75
+ def _payment_methods(self) -> list[Effect]:
76
+ """List payment methods for the processor."""
77
+ # This method should be overridden by subclasses if they support listing payment methods.
78
+ return []
79
+
80
+ def _add_payment_method(self) -> Effect | None:
81
+ """Handle the event when a payment method is added."""
82
+ # This method should be overridden by subclasses to handle specific logic
83
+ # when a payment method is added.
84
+ return None
85
+
86
+ def _remove_payment_method(self) -> Effect | None:
87
+ """Handle the event when a payment method is removed."""
88
+ # This method should be overridden by subclasses to handle specific logic
89
+ # when a payment method is removed.
90
+ return None
91
+
92
+
93
+ __exports__ = ("PaymentProcessor",)
@@ -0,0 +1,200 @@
1
+ from abc import ABC, abstractmethod
2
+ from decimal import Decimal
3
+ from enum import StrEnum
4
+
5
+ from canvas_sdk.effects import Effect
6
+ from canvas_sdk.effects.payment_processor import (
7
+ AddPaymentMethodResponse,
8
+ CardTransaction,
9
+ PaymentMethod,
10
+ PaymentProcessorForm,
11
+ PaymentProcessorMetadata,
12
+ RemovePaymentMethodResponse,
13
+ )
14
+ from canvas_sdk.handlers.payment_processors.base import PaymentProcessor
15
+ from canvas_sdk.v1.data import Patient
16
+
17
+
18
+ class CardPaymentProcessor(PaymentProcessor, ABC):
19
+ """Base Card Payment Processor Handler."""
20
+
21
+ TYPE = PaymentProcessorMetadata.PaymentProcessorType.CARD
22
+
23
+ class PaymentIntent(StrEnum):
24
+ """Enum for payment actions."""
25
+
26
+ ADD_CARD = "add_card"
27
+ PAY = "pay"
28
+
29
+ def _on_payment_processor_selected(self) -> list[Effect]:
30
+ """Handle the event when a payment processor is selected."""
31
+ if self.event.context.get("identifier") == self.identifier:
32
+ intent = self.event.context.get("intent")
33
+ effects = self.on_payment_processor_selected(intent=intent)
34
+ return [effect.apply() for effect in effects]
35
+
36
+ return []
37
+
38
+ def _charge(self) -> Effect | None:
39
+ """Handle the event when a charge is made."""
40
+ if self.event.context.get("identifier") == self.identifier:
41
+ patient = (
42
+ Patient.objects.get(id=self.event.context.get("patient", {}).get("id"))
43
+ if self.event.context.get("patient")
44
+ else None
45
+ )
46
+
47
+ amount = Decimal(str(self.event.context.get("amount")))
48
+ token = str(self.event.context.get("token"))
49
+ effect = self.charge(amount=amount, token=token, patient=patient)
50
+
51
+ return effect.apply() if effect else None
52
+
53
+ return None
54
+
55
+ def _payment_methods(self) -> list[Effect]:
56
+ """List payment methods for the card payment processor."""
57
+ if self.event.context.get("identifier") == self.identifier:
58
+ patient = (
59
+ Patient.objects.get(id=self.event.context.get("patient", {}).get("id"))
60
+ if self.event.context.get("patient")
61
+ else None
62
+ )
63
+ effects = self.payment_methods(patient=patient)
64
+ return [effect.apply() for effect in effects]
65
+
66
+ return []
67
+
68
+ def _add_payment_method(self) -> Effect | None:
69
+ """Handle the event when a card is added."""
70
+ if self.event.context.get("identifier") == self.identifier:
71
+ patient = (
72
+ Patient.objects.get(id=self.event.context.get("patient", {}).get("id"))
73
+ if self.event.context.get("patient")
74
+ else None
75
+ )
76
+
77
+ if not patient:
78
+ return None
79
+
80
+ token = str(self.event.context.get("token"))
81
+ effect = self.add_payment_method(token=token, patient=patient)
82
+ return effect.apply() if effect else None
83
+
84
+ return None
85
+
86
+ def _remove_payment_method(self) -> Effect | None:
87
+ """Handle the event when a payment method is removed."""
88
+ if self.event.context.get("identifier") == self.identifier:
89
+ patient = (
90
+ Patient.objects.get(id=self.event.context.get("patient", {}).get("id"))
91
+ if self.event.context.get("patient")
92
+ else None
93
+ )
94
+
95
+ if not patient:
96
+ return None
97
+
98
+ token = str(self.event.context.get("token"))
99
+ effect = self.remove_payment_method(token=token, patient=patient)
100
+ return effect.apply() if effect else None
101
+
102
+ return None
103
+
104
+ def on_payment_processor_selected(self, intent: str | None) -> list[PaymentProcessorForm]:
105
+ """Handle the event when a payment processor is selected."""
106
+ patient = (
107
+ Patient.objects.get(id=self.event.context.get("patient", {}).get("id"))
108
+ if self.event.context.get("patient")
109
+ else None
110
+ )
111
+ match intent:
112
+ case self.PaymentIntent.ADD_CARD:
113
+ return [self.add_card_form(patient)]
114
+ case self.PaymentIntent.PAY:
115
+ return [self.payment_form(patient)]
116
+ case None:
117
+ return [self.payment_form(patient), self.add_card_form(patient)]
118
+ case _:
119
+ return []
120
+
121
+ @abstractmethod
122
+ def payment_form(self, patient: Patient | None = None) -> PaymentProcessorForm:
123
+ """Return the payment form for the credit card processor.
124
+
125
+ Args:
126
+ patient (Patient | None): The patient for whom the payment is being processed.
127
+
128
+ Returns:
129
+ PaymentProcessorForm: The form for processing the payment.
130
+ """
131
+ raise NotImplementedError("Subclasses must implement the payment_form method.")
132
+
133
+ @abstractmethod
134
+ def add_card_form(self, patient: Patient | None = None) -> PaymentProcessorForm:
135
+ """Return the form for adding a card.
136
+
137
+ Args:
138
+ patient (Patient | None): The patient for whom the add is being added.
139
+
140
+ Returns:
141
+ PaymentProcessorForm: The form for adding a card.
142
+ """
143
+ raise NotImplementedError("Subclasses must implement the add_card_form method.")
144
+
145
+ @abstractmethod
146
+ def charge(
147
+ self, amount: Decimal, token: str, patient: Patient | None = None
148
+ ) -> CardTransaction:
149
+ """Charge a credit/debit card using the provided token.
150
+
151
+ Args:
152
+ amount (Decimal): The amount to charge.
153
+ token (str): The token representing the credit card.
154
+ patient (Patient | None): The patient for whom the charge is being made.
155
+
156
+ Returns:
157
+ CardTransaction: The result of the card transaction.
158
+ """
159
+ raise NotImplementedError("Subclasses must implement the charge method.")
160
+
161
+ @abstractmethod
162
+ def payment_methods(self, patient: Patient | None = None) -> list[PaymentMethod]:
163
+ """List payment methods for the card payment processor.
164
+
165
+ Args:
166
+ patient (Patient | None): The patient for whom the payment methods are being listed.
167
+
168
+ Returns:
169
+ list[PaymentMethod]: A list of payment methods available for the card payment processor.
170
+ """
171
+ raise NotImplementedError("Subclasses must implement the payment_methods method.")
172
+
173
+ @abstractmethod
174
+ def add_payment_method(self, token: str, patient: Patient) -> AddPaymentMethodResponse:
175
+ """Add a payment method for the card payment processor.
176
+
177
+ Args:
178
+ token (str): The token representing the payment method.
179
+ patient (Patient): The patient for whom the payment method is being added.
180
+
181
+ Returns:
182
+ AddPaymentMethodResponse: The response indicating the result of the addition operation.
183
+ """
184
+ raise NotImplementedError("Subclasses must implement the add_payment_method method.")
185
+
186
+ @abstractmethod
187
+ def remove_payment_method(self, token: str, patient: Patient) -> RemovePaymentMethodResponse:
188
+ """Remove a payment method for the card payment processor.
189
+
190
+ Args:
191
+ token (str): The token representing the payment method to be removed.
192
+ patient (Patient): The patient for whom the payment method is being removed.
193
+
194
+ Returns:
195
+ RemovePaymentMethodResponse: The response indicating the result of the removal operation.
196
+ """
197
+ raise NotImplementedError("Subclasses must implement the remove_payment_method method.")
198
+
199
+
200
+ __exports__ = ("CardPaymentProcessor",)
@@ -7,7 +7,7 @@ from typing import Any, TypedDict
7
7
  import yaml
8
8
  from jsonschema import Draft7Validator, validators
9
9
 
10
- from canvas_sdk.utils.plugins import plugin_only
10
+ from canvas_sdk.utils.plugins import plugin_context
11
11
 
12
12
 
13
13
  class Response(TypedDict):
@@ -75,7 +75,7 @@ def extend_with_defaults(validator_class: type[Draft7Validator]) -> type[Draft7V
75
75
  ExtendedDraft7Validator = extend_with_defaults(Draft7Validator)
76
76
 
77
77
 
78
- @plugin_only
78
+ @plugin_context
79
79
  def from_yaml(questionnaire_name: str, **kwargs: Any) -> QuestionnaireConfig | None:
80
80
  """Load a Questionnaire configuration from a YAML file.
81
81