payplus-python 0.1.2__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.
@@ -0,0 +1,117 @@
1
+ """
2
+ PayPlus Payments API - General payment utilities.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Any, Optional
8
+
9
+ from payplus.api.base import BaseAPI
10
+
11
+
12
+ class PaymentsAPI(BaseAPI):
13
+ """
14
+ General Payments API utilities.
15
+ """
16
+
17
+ def check_card(
18
+ self,
19
+ card_number: str,
20
+ expiry_month: str,
21
+ expiry_year: str,
22
+ cvv: Optional[str] = None,
23
+ holder_id: Optional[str] = None,
24
+ **kwargs: Any,
25
+ ) -> dict[str, Any]:
26
+ """
27
+ Validate a card without charging it.
28
+
29
+ Args:
30
+ card_number: Card number
31
+ expiry_month: Expiration month (MM)
32
+ expiry_year: Expiration year (YY or YYYY)
33
+ cvv: CVV (optional)
34
+ holder_id: Card holder ID
35
+
36
+ Returns:
37
+ Card validation result
38
+ """
39
+ data: dict[str, Any] = {
40
+ "credit_card_number": card_number,
41
+ "card_date_mmyy": f"{expiry_month}{expiry_year[-2:]}",
42
+ }
43
+
44
+ if cvv:
45
+ data["card_cvv"] = cvv
46
+ if holder_id:
47
+ data["holder_id"] = holder_id
48
+
49
+ if self._client.terminal_uid:
50
+ data["terminal_uid"] = self._client.terminal_uid
51
+
52
+ return self._request("POST", "Payments/CheckCard", data)
53
+
54
+ def tokenize(
55
+ self,
56
+ card_number: str,
57
+ expiry_month: str,
58
+ expiry_year: str,
59
+ cvv: Optional[str] = None,
60
+ holder_name: Optional[str] = None,
61
+ holder_id: Optional[str] = None,
62
+ **kwargs: Any,
63
+ ) -> dict[str, Any]:
64
+ """
65
+ Tokenize a card for future charges.
66
+
67
+ Args:
68
+ card_number: Card number
69
+ expiry_month: Expiration month (MM)
70
+ expiry_year: Expiration year (YY or YYYY)
71
+ cvv: CVV
72
+ holder_name: Card holder name
73
+ holder_id: Card holder ID
74
+
75
+ Returns:
76
+ Tokenization result with card_uid
77
+ """
78
+ data: dict[str, Any] = {
79
+ "credit_card_number": card_number,
80
+ "card_date_mmyy": f"{expiry_month}{expiry_year[-2:]}",
81
+ }
82
+
83
+ if cvv:
84
+ data["card_cvv"] = cvv
85
+ if holder_name:
86
+ data["card_holder_name"] = holder_name
87
+ if holder_id:
88
+ data["holder_id"] = holder_id
89
+
90
+ if self._client.terminal_uid:
91
+ data["terminal_uid"] = self._client.terminal_uid
92
+
93
+ return self._request("POST", "Payments/Tokenize", data)
94
+
95
+ def get_token(self, token_uid: str) -> dict[str, Any]:
96
+ """
97
+ Get tokenized card details.
98
+
99
+ Args:
100
+ token_uid: Card token UID
101
+
102
+ Returns:
103
+ Token details (masked card info)
104
+ """
105
+ return self._request("GET", f"Payments/Token/{token_uid}")
106
+
107
+ def delete_token(self, token_uid: str) -> dict[str, Any]:
108
+ """
109
+ Delete a card token.
110
+
111
+ Args:
112
+ token_uid: Card token UID
113
+
114
+ Returns:
115
+ Deletion result
116
+ """
117
+ return self._request("DELETE", f"Payments/Token/{token_uid}")
@@ -0,0 +1,216 @@
1
+ """
2
+ PayPlus Recurring Payments API.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Any, Optional
8
+ from decimal import Decimal
9
+ from datetime import date, datetime
10
+
11
+ from payplus.api.base import BaseAPI
12
+
13
+
14
+ class RecurringAPI(BaseAPI):
15
+ """
16
+ Recurring Payments API for creating and managing recurring charges.
17
+ """
18
+
19
+ def add(
20
+ self,
21
+ token: str,
22
+ amount: float | Decimal,
23
+ currency: str = "ILS",
24
+ description: Optional[str] = None,
25
+ start_date: Optional[date | datetime | str] = None,
26
+ end_date: Optional[date | datetime | str] = None,
27
+ interval: str = "month",
28
+ interval_count: int = 1,
29
+ initial_amount: Optional[float | Decimal] = None,
30
+ customer_uid: Optional[str] = None,
31
+ customer_email: Optional[str] = None,
32
+ customer_name: Optional[str] = None,
33
+ payments: int = 1,
34
+ more_info: Optional[str] = None,
35
+ **kwargs: Any,
36
+ ) -> dict[str, Any]:
37
+ """
38
+ Create a recurring payment.
39
+
40
+ Args:
41
+ token: Card token from initial payment
42
+ amount: Recurring amount
43
+ currency: Currency code (default: ILS)
44
+ description: Payment description
45
+ start_date: When to start the recurring
46
+ end_date: When to end the recurring
47
+ interval: Billing interval (day/week/month/year)
48
+ interval_count: Number of intervals between charges
49
+ initial_amount: Initial charge amount (if different)
50
+ customer_uid: Customer UID
51
+ customer_email: Customer email
52
+ customer_name: Customer name
53
+ payments: Number of installments per charge
54
+ more_info: Custom field
55
+
56
+ Returns:
57
+ API response with recurring payment details
58
+ """
59
+ data: dict[str, Any] = {
60
+ "card_uid": token,
61
+ "amount": float(amount),
62
+ "currency_code": currency,
63
+ "recurring_type": self._get_recurring_type(interval),
64
+ "recurring_amount": interval_count,
65
+ "payments": payments,
66
+ }
67
+
68
+ if description:
69
+ data["more_info"] = description
70
+ if more_info:
71
+ data["more_info"] = more_info
72
+
73
+ if start_date:
74
+ data["start_date"] = self._format_date(start_date)
75
+ if end_date:
76
+ data["end_date"] = self._format_date(end_date)
77
+
78
+ if initial_amount is not None:
79
+ data["initial_amount"] = float(initial_amount)
80
+
81
+ # Customer info
82
+ customer = {}
83
+ if customer_uid:
84
+ customer["customer_uid"] = customer_uid
85
+ if customer_email:
86
+ customer["email"] = customer_email
87
+ if customer_name:
88
+ customer["customer_name"] = customer_name
89
+ if customer:
90
+ data["customer"] = customer
91
+
92
+ if self._client.terminal_uid:
93
+ data["terminal_uid"] = self._client.terminal_uid
94
+
95
+ for key, value in kwargs.items():
96
+ if key not in data and value is not None:
97
+ data[key] = value
98
+
99
+ return self._request("POST", "RecurringPayments/Add", data)
100
+
101
+ async def async_add(
102
+ self,
103
+ token: str,
104
+ amount: float | Decimal,
105
+ **kwargs: Any,
106
+ ) -> dict[str, Any]:
107
+ """Async version of add."""
108
+ data: dict[str, Any] = {
109
+ "card_uid": token,
110
+ "amount": float(amount),
111
+ "currency_code": kwargs.get("currency", "ILS"),
112
+ "recurring_type": self._get_recurring_type(kwargs.get("interval", "month")),
113
+ "recurring_amount": kwargs.get("interval_count", 1),
114
+ "payments": kwargs.get("payments", 1),
115
+ }
116
+
117
+ if self._client.terminal_uid:
118
+ data["terminal_uid"] = self._client.terminal_uid
119
+
120
+ return await self._async_request("POST", "RecurringPayments/Add", data)
121
+
122
+ def charge(
123
+ self,
124
+ recurring_uid: str,
125
+ amount: Optional[float | Decimal] = None,
126
+ **kwargs: Any,
127
+ ) -> dict[str, Any]:
128
+ """
129
+ Manually charge a recurring payment.
130
+
131
+ Args:
132
+ recurring_uid: Recurring payment UID
133
+ amount: Amount to charge (uses default if not specified)
134
+
135
+ Returns:
136
+ Charge result
137
+ """
138
+ data: dict[str, Any] = {
139
+ "recurring_uid": recurring_uid,
140
+ }
141
+
142
+ if amount is not None:
143
+ data["amount"] = float(amount)
144
+
145
+ return self._request("POST", "RecurringPayments/Charge", data)
146
+
147
+ def cancel(self, recurring_uid: str) -> dict[str, Any]:
148
+ """
149
+ Cancel a recurring payment.
150
+
151
+ Args:
152
+ recurring_uid: Recurring payment UID
153
+
154
+ Returns:
155
+ Cancellation result
156
+ """
157
+ return self._request("POST", "RecurringPayments/Cancel", {
158
+ "recurring_uid": recurring_uid,
159
+ })
160
+
161
+ def get(self, recurring_uid: str) -> dict[str, Any]:
162
+ """
163
+ Get recurring payment details.
164
+
165
+ Args:
166
+ recurring_uid: Recurring payment UID
167
+
168
+ Returns:
169
+ Recurring payment details
170
+ """
171
+ return self._request("GET", f"RecurringPayments/{recurring_uid}")
172
+
173
+ def list(
174
+ self,
175
+ page: int = 1,
176
+ page_size: int = 50,
177
+ status: Optional[str] = None,
178
+ **kwargs: Any,
179
+ ) -> dict[str, Any]:
180
+ """
181
+ List recurring payments.
182
+
183
+ Args:
184
+ page: Page number
185
+ page_size: Items per page
186
+ status: Filter by status
187
+
188
+ Returns:
189
+ List of recurring payments
190
+ """
191
+ params = {
192
+ "page": page,
193
+ "page_size": page_size,
194
+ }
195
+ if status:
196
+ params["status"] = status
197
+
198
+ return self._request("GET", "RecurringPayments", params=params)
199
+
200
+ def _get_recurring_type(self, interval: str) -> int:
201
+ """Convert interval string to PayPlus recurring type."""
202
+ mapping = {
203
+ "day": 1,
204
+ "week": 2,
205
+ "month": 3,
206
+ "year": 4,
207
+ }
208
+ return mapping.get(interval.lower(), 3) # Default to monthly
209
+
210
+ def _format_date(self, d: date | datetime | str) -> str:
211
+ """Format date for PayPlus API."""
212
+ if isinstance(d, str):
213
+ return d
214
+ if isinstance(d, datetime):
215
+ return d.strftime("%Y-%m-%d")
216
+ return d.strftime("%Y-%m-%d")
@@ -0,0 +1,203 @@
1
+ """
2
+ PayPlus Transactions API.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Any, Optional
8
+ from decimal import Decimal
9
+
10
+ from payplus.api.base import BaseAPI
11
+
12
+
13
+ class TransactionsAPI(BaseAPI):
14
+ """
15
+ Transactions API for direct charges and transaction management.
16
+ """
17
+
18
+ def charge(
19
+ self,
20
+ token: str,
21
+ amount: float | Decimal,
22
+ currency: str = "ILS",
23
+ cvv: Optional[str] = None,
24
+ payments: int = 1,
25
+ description: Optional[str] = None,
26
+ customer_uid: Optional[str] = None,
27
+ more_info: Optional[str] = None,
28
+ more_info_1: Optional[str] = None,
29
+ more_info_2: Optional[str] = None,
30
+ **kwargs: Any,
31
+ ) -> dict[str, Any]:
32
+ """
33
+ Charge a tokenized card directly (J4 transaction).
34
+
35
+ Args:
36
+ token: Card token
37
+ amount: Amount to charge
38
+ currency: Currency code (default: ILS)
39
+ cvv: CVV if required
40
+ payments: Number of installments
41
+ description: Payment description
42
+ customer_uid: Customer UID
43
+ more_info: Custom field 1
44
+ more_info_1: Custom field 2
45
+ more_info_2: Custom field 3
46
+
47
+ Returns:
48
+ Transaction result
49
+ """
50
+ data: dict[str, Any] = {
51
+ "card_uid": token,
52
+ "amount": float(amount),
53
+ "currency_code": currency,
54
+ "payments": payments,
55
+ }
56
+
57
+ if cvv:
58
+ data["card_cvv"] = cvv
59
+
60
+ if description:
61
+ data["more_info"] = description
62
+ if more_info:
63
+ data["more_info"] = more_info
64
+ if more_info_1:
65
+ data["more_info_1"] = more_info_1
66
+ if more_info_2:
67
+ data["more_info_2"] = more_info_2
68
+
69
+ if customer_uid:
70
+ data["customer_uid"] = customer_uid
71
+
72
+ if self._client.terminal_uid:
73
+ data["terminal_uid"] = self._client.terminal_uid
74
+
75
+ for key, value in kwargs.items():
76
+ if key not in data and value is not None:
77
+ data[key] = value
78
+
79
+ return self._request("POST", "Transactions/Charge", data)
80
+
81
+ async def async_charge(
82
+ self,
83
+ token: str,
84
+ amount: float | Decimal,
85
+ **kwargs: Any,
86
+ ) -> dict[str, Any]:
87
+ """Async version of charge."""
88
+ data: dict[str, Any] = {
89
+ "card_uid": token,
90
+ "amount": float(amount),
91
+ "currency_code": kwargs.get("currency", "ILS"),
92
+ "payments": kwargs.get("payments", 1),
93
+ }
94
+
95
+ if self._client.terminal_uid:
96
+ data["terminal_uid"] = self._client.terminal_uid
97
+
98
+ return await self._async_request("POST", "Transactions/Charge", data)
99
+
100
+ def get(self, transaction_uid: str) -> dict[str, Any]:
101
+ """
102
+ Get transaction details.
103
+
104
+ Args:
105
+ transaction_uid: Transaction UID
106
+
107
+ Returns:
108
+ Transaction details
109
+ """
110
+ return self._request("GET", f"Transactions/{transaction_uid}")
111
+
112
+ def refund(
113
+ self,
114
+ transaction_uid: str,
115
+ amount: Optional[float | Decimal] = None,
116
+ **kwargs: Any,
117
+ ) -> dict[str, Any]:
118
+ """
119
+ Refund a transaction.
120
+
121
+ Args:
122
+ transaction_uid: Transaction UID
123
+ amount: Amount to refund (full refund if not specified)
124
+
125
+ Returns:
126
+ Refund result
127
+ """
128
+ data: dict[str, Any] = {
129
+ "transaction_uid": transaction_uid,
130
+ }
131
+
132
+ if amount is not None:
133
+ data["amount"] = float(amount)
134
+
135
+ return self._request("POST", "Transactions/Refund", data)
136
+
137
+ def approve(
138
+ self,
139
+ approval_number: str,
140
+ amount: float | Decimal,
141
+ token: str,
142
+ currency: str = "ILS",
143
+ **kwargs: Any,
144
+ ) -> dict[str, Any]:
145
+ """
146
+ Complete a pre-authorized transaction (J5).
147
+
148
+ Args:
149
+ approval_number: Approval number from authorization
150
+ amount: Amount to capture
151
+ token: Card token
152
+ currency: Currency code
153
+
154
+ Returns:
155
+ Capture result
156
+ """
157
+ data: dict[str, Any] = {
158
+ "approval_number": approval_number,
159
+ "amount": float(amount),
160
+ "card_uid": token,
161
+ "currency_code": currency,
162
+ }
163
+
164
+ if self._client.terminal_uid:
165
+ data["terminal_uid"] = self._client.terminal_uid
166
+
167
+ return self._request("POST", "Transactions/Approve", data)
168
+
169
+ def list(
170
+ self,
171
+ page: int = 1,
172
+ page_size: int = 50,
173
+ from_date: Optional[str] = None,
174
+ to_date: Optional[str] = None,
175
+ status: Optional[str] = None,
176
+ **kwargs: Any,
177
+ ) -> dict[str, Any]:
178
+ """
179
+ List transactions.
180
+
181
+ Args:
182
+ page: Page number
183
+ page_size: Items per page
184
+ from_date: Start date filter (YYYY-MM-DD)
185
+ to_date: End date filter (YYYY-MM-DD)
186
+ status: Filter by status
187
+
188
+ Returns:
189
+ List of transactions
190
+ """
191
+ params: dict[str, Any] = {
192
+ "page": page,
193
+ "page_size": page_size,
194
+ }
195
+
196
+ if from_date:
197
+ params["from_date"] = from_date
198
+ if to_date:
199
+ params["to_date"] = to_date
200
+ if status:
201
+ params["status"] = status
202
+
203
+ return self._request("GET", "Transactions", params=params)