kirimel-python 0.1.7__tar.gz → 2.0.1__tar.gz

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.
Files changed (22) hide show
  1. {kirimel_python-0.1.7 → kirimel_python-2.0.1}/PKG-INFO +204 -3
  2. {kirimel_python-0.1.7 → kirimel_python-2.0.1}/README.md +203 -2
  3. {kirimel_python-0.1.7 → kirimel_python-2.0.1}/kirimel/__init__.py +1 -0
  4. {kirimel_python-0.1.7 → kirimel_python-2.0.1}/kirimel/client.py +79 -0
  5. {kirimel_python-0.1.7 → kirimel_python-2.0.1}/kirimel/exceptions.py +15 -2
  6. {kirimel_python-0.1.7 → kirimel_python-2.0.1}/kirimel/http_client.py +11 -4
  7. kirimel_python-2.0.1/kirimel/loyalty_http_client.py +141 -0
  8. {kirimel_python-0.1.7 → kirimel_python-2.0.1}/kirimel/resources/__init__.py +72 -4
  9. kirimel_python-2.0.1/kirimel/resources/loyalty/__init__.py +8 -0
  10. kirimel_python-2.0.1/kirimel/resources/loyalty/customers.py +110 -0
  11. kirimel_python-2.0.1/kirimel/resources/loyalty/points.py +58 -0
  12. kirimel_python-2.0.1/kirimel/resources/loyalty/vouchers.py +70 -0
  13. kirimel_python-2.0.1/kirimel/resources/loyalty/wallet.py +34 -0
  14. {kirimel_python-0.1.7 → kirimel_python-2.0.1}/kirimel_python.egg-info/PKG-INFO +204 -3
  15. {kirimel_python-0.1.7 → kirimel_python-2.0.1}/kirimel_python.egg-info/SOURCES.txt +6 -0
  16. {kirimel_python-0.1.7 → kirimel_python-2.0.1}/pyproject.toml +2 -2
  17. {kirimel_python-0.1.7 → kirimel_python-2.0.1}/LICENSE +0 -0
  18. {kirimel_python-0.1.7 → kirimel_python-2.0.1}/kirimel_python.egg-info/dependency_links.txt +0 -0
  19. {kirimel_python-0.1.7 → kirimel_python-2.0.1}/kirimel_python.egg-info/requires.txt +0 -0
  20. {kirimel_python-0.1.7 → kirimel_python-2.0.1}/kirimel_python.egg-info/top_level.txt +0 -0
  21. {kirimel_python-0.1.7 → kirimel_python-2.0.1}/setup.cfg +0 -0
  22. {kirimel_python-0.1.7 → kirimel_python-2.0.1}/tests/test_client.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kirimel-python
3
- Version: 0.1.7
3
+ Version: 2.0.1
4
4
  Summary: Official KiriMel Python SDK
5
5
  Author-email: KiriMel <support@kirimel.com>
6
6
  License: MIT
@@ -30,7 +30,15 @@ Dynamic: license-file
30
30
 
31
31
  # KiriMel Python SDK
32
32
 
33
- Official Python SDK for KiriMel Email Marketing API.
33
+ Official Python SDK for KiriMel Email Marketing API & Loyalty API.
34
+
35
+ ## Features
36
+
37
+ - **Email API**: Manage campaigns, subscribers, lists, templates, forms, workflows & more
38
+ - **Loyalty API**: Customer loyalty with points, vouchers, tiers, and wallet management
39
+ - **Unified Client**: Single SDK for both APIs with different authentication methods
40
+ - **Type Hints**: Full type annotations for Python 3.8+
41
+ - **Retry Logic**: Built-in exponential backoff for failed requests
34
42
 
35
43
  ## Installation
36
44
 
@@ -454,6 +462,199 @@ node_types = client.workflows.node_types()
454
462
  data = client.workflows.get_data(id)
455
463
  ```
456
464
 
465
+ ### Email (Transactional)
466
+
467
+ Send transactional emails with attachment support:
468
+
469
+ ```python
470
+ # Send transactional email
471
+ result = client.email.send({
472
+ 'to': 'user@example.com',
473
+ 'subject': 'Welcome to our service!',
474
+ 'html': '<h1>Welcome!</h1><p>Thanks for signing up.</p>',
475
+ 'text': 'Welcome! Thanks for signing up.',
476
+ 'from_name': 'My App',
477
+ 'reply_to': 'support@example.com',
478
+ 'cc': 'cc@example.com',
479
+ 'bcc': ['bcc1@example.com', 'bcc2@example.com'],
480
+ 'attachments': [
481
+ {
482
+ 'name': 'invoice.pdf',
483
+ 'content': base64.b64encode(open('/path/to/invoice.pdf', 'rb').read()).decode()
484
+ },
485
+ {
486
+ 'name': 'report.csv',
487
+ 'content': base64.b64encode(open('/path/to/report.csv', 'rb').read()).decode()
488
+ }
489
+ ]
490
+ })
491
+
492
+ # Response includes message_id and tracking_id
493
+ print(f"Message ID: {result['message_id']}")
494
+ print(f"Tracking ID: {result['tracking_id']}")
495
+
496
+ # Send to multiple recipients
497
+ result = client.email.send({
498
+ 'to': ['user1@example.com', 'user2@example.com'],
499
+ 'subject': 'Team update',
500
+ 'html': '<p>Here is the latest update.</p>'
501
+ })
502
+
503
+ # Get SES send quota
504
+ quota = client.email.quota()
505
+ print(f"Remaining: {quota['remaining']}")
506
+ print(f"Max 24h: {quota['max_24_hour_send']}")
507
+ print(f"Utilization: {quota['utilization_percent']}%")
508
+
509
+ # Get verified emails
510
+ verified = client.email.verified_emails()
511
+
512
+ # Verify a new email address
513
+ result = client.email.verify_email('new@example.com')
514
+ ```
515
+
516
+ ## Loyalty API
517
+
518
+ The Loyalty API uses HMAC SHA256 signature authentication for secure POS integration. The SDK handles signature calculation automatically.
519
+
520
+ ### Authentication
521
+
522
+ ```python
523
+ # Email API credentials (required)
524
+ client = kirimel.KiriMel(api_key='sk_test_xxx')
525
+
526
+ # Loyalty API credentials (optional - only if using loyalty features)
527
+ client = kirimel.KiriMel(
528
+ api_key='sk_test_xxx',
529
+ client_key='cli_test_xxx', # Or KIRIMEL_LOYALTY_CLIENT_KEY env var
530
+ client_secret='your_secret_here' # Or KIRIMEL_LOYALTY_CLIENT_SECRET env var
531
+ )
532
+ ```
533
+
534
+ ### Customers
535
+
536
+ ```python
537
+ # Register a new customer
538
+ customer = client.loyalty_customers.register({
539
+ 'phone': '+60123456789',
540
+ 'name': 'John Doe',
541
+ 'email': 'john@example.com',
542
+ 'birth_date': '1990-05-15', # Optional
543
+ 'qr_code': 'CUSTOMER_123' # Optional
544
+ })
545
+
546
+ # Look up customer by phone
547
+ customer = client.loyalty_customers.lookup({
548
+ 'phone': '+60123456789'
549
+ })
550
+
551
+ # Get customer profile
552
+ profile = client.loyalty_customers.get(customer_id)
553
+
554
+ # Get customer transactions
555
+ transactions = client.loyalty_customers.transactions(customer_id)
556
+
557
+ # Manually adjust points
558
+ adjustment = client.loyalty_customers.adjust(customer_id, {
559
+ 'points': 50,
560
+ 'reference': 'MANUAL_ADJUST_001',
561
+ 'description': 'Goodwill gesture',
562
+ 'adjusted_by': 'Admin'
563
+ })
564
+
565
+ # Get customer tier
566
+ tier = client.loyalty_customers.tier(customer_id)
567
+
568
+ # List customers
569
+ customers = client.loyalty_customers.list({
570
+ 'page': 1,
571
+ 'per_page': 50,
572
+ 'tier': 'gold'
573
+ })
574
+ ```
575
+
576
+ ### Points & Wallet
577
+
578
+ ```python
579
+ # Award points
580
+ earn = client.loyalty_points.earn({
581
+ 'customer_id': customer_id,
582
+ 'points': 100,
583
+ 'amount': 50.50,
584
+ 'reference_id': 'ORDER_123',
585
+ 'description': 'Purchase reward'
586
+ })
587
+
588
+ # Preview redemption (check before confirming)
589
+ preview = client.loyalty_points.preview_redeem({
590
+ 'customer_id': customer_id,
591
+ 'points_to_redeem': 100
592
+ })
593
+ # Returns: points_value, max_redeemable, amount_discount
594
+
595
+ # Confirm redemption
596
+ redeem = client.loyalty_points.commit_redeem({
597
+ 'customer_id': customer_id,
598
+ 'points_to_redeem': 100,
599
+ 'reference_id': 'BILL_456'
600
+ })
601
+
602
+ # Reverse transaction (if needed)
603
+ reverse = client.loyalty_points.reverse({
604
+ 'transaction_id': transaction_id,
605
+ 'reason': 'Customer return',
606
+ 'reference_id': 'RETURN_123'
607
+ })
608
+
609
+ # Get wallet balance
610
+ balance = client.loyalty_wallet.balance({
611
+ 'customer_id': customer_id
612
+ })
613
+
614
+ # Recalculate balance from ledger
615
+ recalc = client.loyalty_wallet.recalculate({
616
+ 'customer_id': customer_id
617
+ })
618
+ ```
619
+
620
+ ### Vouchers
621
+
622
+ ```python
623
+ # Create voucher batch
624
+ batch = client.loyalty_vouchers.create_batch({
625
+ 'name': 'Grand Opening Promo',
626
+ 'type': 'PERCENT', # or 'FIXED'
627
+ 'value': 10,
628
+ 'quantity': 100,
629
+ 'valid_from': '2024-06-01',
630
+ 'valid_until': '2024-12-31',
631
+ 'min_purchase': 50.00,
632
+ 'max_discount': 25.00
633
+ })
634
+
635
+ # List voucher batches
636
+ batches = client.loyalty_vouchers.list_batches()
637
+
638
+ # Issue voucher to customer
639
+ issue = client.loyalty_vouchers.issue({
640
+ 'voucher_batch_id': batch_id,
641
+ 'customer_id': customer_id,
642
+ 'delivered_via': 'email', # or 'sms'
643
+ 'reference_id': 'PROMO_001'
644
+ })
645
+
646
+ # Redeem voucher
647
+ redeem = client.loyalty_vouchers.redeem({
648
+ 'code': 'VOUCHER_A1B2C3D4E5F6',
649
+ 'customer_id': customer_id,
650
+ 'purchase_amount': 75.00,
651
+ 'reference_id': 'ORDER_789'
652
+ })
653
+
654
+ # Get voucher details
655
+ voucher = client.loyalty_vouchers.get('VOUCHER_A1B2C3D4E5F6')
656
+ ```
657
+
457
658
  ## Error Handling
458
659
 
459
660
  ```python
@@ -502,6 +703,6 @@ MIT License
502
703
 
503
704
  ## Support
504
705
 
505
- - Documentation: https://docs.kirimel.com
706
+ - Documentation: https://kirimel.com/api-docs
506
707
  - GitHub: https://github.com/hualiglobal/kirimel-python-sdk
507
708
  - Issues: https://github.com/hualiglobal/kirimel-python-sdk/issues
@@ -1,6 +1,14 @@
1
1
  # KiriMel Python SDK
2
2
 
3
- Official Python SDK for KiriMel Email Marketing API.
3
+ Official Python SDK for KiriMel Email Marketing API & Loyalty API.
4
+
5
+ ## Features
6
+
7
+ - **Email API**: Manage campaigns, subscribers, lists, templates, forms, workflows & more
8
+ - **Loyalty API**: Customer loyalty with points, vouchers, tiers, and wallet management
9
+ - **Unified Client**: Single SDK for both APIs with different authentication methods
10
+ - **Type Hints**: Full type annotations for Python 3.8+
11
+ - **Retry Logic**: Built-in exponential backoff for failed requests
4
12
 
5
13
  ## Installation
6
14
 
@@ -424,6 +432,199 @@ node_types = client.workflows.node_types()
424
432
  data = client.workflows.get_data(id)
425
433
  ```
426
434
 
435
+ ### Email (Transactional)
436
+
437
+ Send transactional emails with attachment support:
438
+
439
+ ```python
440
+ # Send transactional email
441
+ result = client.email.send({
442
+ 'to': 'user@example.com',
443
+ 'subject': 'Welcome to our service!',
444
+ 'html': '<h1>Welcome!</h1><p>Thanks for signing up.</p>',
445
+ 'text': 'Welcome! Thanks for signing up.',
446
+ 'from_name': 'My App',
447
+ 'reply_to': 'support@example.com',
448
+ 'cc': 'cc@example.com',
449
+ 'bcc': ['bcc1@example.com', 'bcc2@example.com'],
450
+ 'attachments': [
451
+ {
452
+ 'name': 'invoice.pdf',
453
+ 'content': base64.b64encode(open('/path/to/invoice.pdf', 'rb').read()).decode()
454
+ },
455
+ {
456
+ 'name': 'report.csv',
457
+ 'content': base64.b64encode(open('/path/to/report.csv', 'rb').read()).decode()
458
+ }
459
+ ]
460
+ })
461
+
462
+ # Response includes message_id and tracking_id
463
+ print(f"Message ID: {result['message_id']}")
464
+ print(f"Tracking ID: {result['tracking_id']}")
465
+
466
+ # Send to multiple recipients
467
+ result = client.email.send({
468
+ 'to': ['user1@example.com', 'user2@example.com'],
469
+ 'subject': 'Team update',
470
+ 'html': '<p>Here is the latest update.</p>'
471
+ })
472
+
473
+ # Get SES send quota
474
+ quota = client.email.quota()
475
+ print(f"Remaining: {quota['remaining']}")
476
+ print(f"Max 24h: {quota['max_24_hour_send']}")
477
+ print(f"Utilization: {quota['utilization_percent']}%")
478
+
479
+ # Get verified emails
480
+ verified = client.email.verified_emails()
481
+
482
+ # Verify a new email address
483
+ result = client.email.verify_email('new@example.com')
484
+ ```
485
+
486
+ ## Loyalty API
487
+
488
+ The Loyalty API uses HMAC SHA256 signature authentication for secure POS integration. The SDK handles signature calculation automatically.
489
+
490
+ ### Authentication
491
+
492
+ ```python
493
+ # Email API credentials (required)
494
+ client = kirimel.KiriMel(api_key='sk_test_xxx')
495
+
496
+ # Loyalty API credentials (optional - only if using loyalty features)
497
+ client = kirimel.KiriMel(
498
+ api_key='sk_test_xxx',
499
+ client_key='cli_test_xxx', # Or KIRIMEL_LOYALTY_CLIENT_KEY env var
500
+ client_secret='your_secret_here' # Or KIRIMEL_LOYALTY_CLIENT_SECRET env var
501
+ )
502
+ ```
503
+
504
+ ### Customers
505
+
506
+ ```python
507
+ # Register a new customer
508
+ customer = client.loyalty_customers.register({
509
+ 'phone': '+60123456789',
510
+ 'name': 'John Doe',
511
+ 'email': 'john@example.com',
512
+ 'birth_date': '1990-05-15', # Optional
513
+ 'qr_code': 'CUSTOMER_123' # Optional
514
+ })
515
+
516
+ # Look up customer by phone
517
+ customer = client.loyalty_customers.lookup({
518
+ 'phone': '+60123456789'
519
+ })
520
+
521
+ # Get customer profile
522
+ profile = client.loyalty_customers.get(customer_id)
523
+
524
+ # Get customer transactions
525
+ transactions = client.loyalty_customers.transactions(customer_id)
526
+
527
+ # Manually adjust points
528
+ adjustment = client.loyalty_customers.adjust(customer_id, {
529
+ 'points': 50,
530
+ 'reference': 'MANUAL_ADJUST_001',
531
+ 'description': 'Goodwill gesture',
532
+ 'adjusted_by': 'Admin'
533
+ })
534
+
535
+ # Get customer tier
536
+ tier = client.loyalty_customers.tier(customer_id)
537
+
538
+ # List customers
539
+ customers = client.loyalty_customers.list({
540
+ 'page': 1,
541
+ 'per_page': 50,
542
+ 'tier': 'gold'
543
+ })
544
+ ```
545
+
546
+ ### Points & Wallet
547
+
548
+ ```python
549
+ # Award points
550
+ earn = client.loyalty_points.earn({
551
+ 'customer_id': customer_id,
552
+ 'points': 100,
553
+ 'amount': 50.50,
554
+ 'reference_id': 'ORDER_123',
555
+ 'description': 'Purchase reward'
556
+ })
557
+
558
+ # Preview redemption (check before confirming)
559
+ preview = client.loyalty_points.preview_redeem({
560
+ 'customer_id': customer_id,
561
+ 'points_to_redeem': 100
562
+ })
563
+ # Returns: points_value, max_redeemable, amount_discount
564
+
565
+ # Confirm redemption
566
+ redeem = client.loyalty_points.commit_redeem({
567
+ 'customer_id': customer_id,
568
+ 'points_to_redeem': 100,
569
+ 'reference_id': 'BILL_456'
570
+ })
571
+
572
+ # Reverse transaction (if needed)
573
+ reverse = client.loyalty_points.reverse({
574
+ 'transaction_id': transaction_id,
575
+ 'reason': 'Customer return',
576
+ 'reference_id': 'RETURN_123'
577
+ })
578
+
579
+ # Get wallet balance
580
+ balance = client.loyalty_wallet.balance({
581
+ 'customer_id': customer_id
582
+ })
583
+
584
+ # Recalculate balance from ledger
585
+ recalc = client.loyalty_wallet.recalculate({
586
+ 'customer_id': customer_id
587
+ })
588
+ ```
589
+
590
+ ### Vouchers
591
+
592
+ ```python
593
+ # Create voucher batch
594
+ batch = client.loyalty_vouchers.create_batch({
595
+ 'name': 'Grand Opening Promo',
596
+ 'type': 'PERCENT', # or 'FIXED'
597
+ 'value': 10,
598
+ 'quantity': 100,
599
+ 'valid_from': '2024-06-01',
600
+ 'valid_until': '2024-12-31',
601
+ 'min_purchase': 50.00,
602
+ 'max_discount': 25.00
603
+ })
604
+
605
+ # List voucher batches
606
+ batches = client.loyalty_vouchers.list_batches()
607
+
608
+ # Issue voucher to customer
609
+ issue = client.loyalty_vouchers.issue({
610
+ 'voucher_batch_id': batch_id,
611
+ 'customer_id': customer_id,
612
+ 'delivered_via': 'email', # or 'sms'
613
+ 'reference_id': 'PROMO_001'
614
+ })
615
+
616
+ # Redeem voucher
617
+ redeem = client.loyalty_vouchers.redeem({
618
+ 'code': 'VOUCHER_A1B2C3D4E5F6',
619
+ 'customer_id': customer_id,
620
+ 'purchase_amount': 75.00,
621
+ 'reference_id': 'ORDER_789'
622
+ })
623
+
624
+ # Get voucher details
625
+ voucher = client.loyalty_vouchers.get('VOUCHER_A1B2C3D4E5F6')
626
+ ```
627
+
427
628
  ## Error Handling
428
629
 
429
630
  ```python
@@ -472,6 +673,6 @@ MIT License
472
673
 
473
674
  ## Support
474
675
 
475
- - Documentation: https://docs.kirimel.com
676
+ - Documentation: https://kirimel.com/api-docs
476
677
  - GitHub: https://github.com/hualiglobal/kirimel-python-sdk
477
678
  - Issues: https://github.com/hualiglobal/kirimel-python-sdk/issues
@@ -3,6 +3,7 @@ KiriMel Python SDK
3
3
 
4
4
  Official Python SDK for KiriMel Email Marketing API.
5
5
  """
6
+
6
7
  from .client import KiriMel
7
8
  from .exceptions import (
8
9
  ApiException,
@@ -1,8 +1,11 @@
1
1
  """
2
2
  KiriMel Python SDK Client
3
3
  """
4
+
5
+ import os
4
6
  from typing import Optional
5
7
  from .http_client import HttpClient
8
+ from .loyalty_http_client import LoyaltyHttpClient
6
9
  from .resources import (
7
10
  Campaigns,
8
11
  Subscribers,
@@ -14,6 +17,13 @@ from .resources import (
14
17
  LandingPages,
15
18
  Workflows,
16
19
  Webhooks,
20
+ Email,
21
+ )
22
+ from .resources.loyalty import (
23
+ Customers as LoyaltyCustomers,
24
+ Points as LoyaltyPoints,
25
+ Vouchers as LoyaltyVouchers,
26
+ Wallet as LoyaltyWallet,
17
27
  )
18
28
 
19
29
 
@@ -33,6 +43,8 @@ class KiriMel:
33
43
  base_url: str = "https://kirimel.com/api",
34
44
  timeout: int = 30,
35
45
  retries: int = 3,
46
+ client_key: Optional[str] = None,
47
+ client_secret: Optional[str] = None,
36
48
  ):
37
49
  """
38
50
  Create a new API client
@@ -42,6 +54,8 @@ class KiriMel:
42
54
  base_url: Base URL (default: https://kirimel.com/api)
43
55
  timeout: Request timeout in seconds (default: 30)
44
56
  retries: Number of retries (default: 3)
57
+ client_key: Loyalty API client key (or use KIRIMEL_LOYALTY_CLIENT_KEY env var)
58
+ client_secret: Loyalty API client secret (or use KIRIMEL_LOYALTY_CLIENT_SECRET env var)
45
59
  """
46
60
  self._http_client = HttpClient(
47
61
  api_key=api_key,
@@ -59,6 +73,32 @@ class KiriMel:
59
73
  self._landing_pages: Optional[LandingPages] = None
60
74
  self._workflows: Optional[Workflows] = None
61
75
  self._webhooks: Optional[Webhooks] = None
76
+ self._email: Optional[Email] = None
77
+
78
+ # Loyalty API clients
79
+ self._loyalty_http_client: Optional[LoyaltyHttpClient] = None
80
+ self._loyalty_customers: Optional[LoyaltyCustomers] = None
81
+ self._loyalty_points: Optional[LoyaltyPoints] = None
82
+ self._loyalty_vouchers: Optional[LoyaltyVouchers] = None
83
+ self._loyalty_wallet: Optional[LoyaltyWallet] = None
84
+
85
+ # Store credentials for lazy initialization
86
+ self._loyalty_base_url = base_url.replace("/api", "")
87
+ self._loyalty_client_key = client_key or os.getenv("KIRIMEL_LOYALTY_CLIENT_KEY")
88
+ self._loyalty_client_secret = client_secret or os.getenv("KIRIMEL_LOYALTY_CLIENT_SECRET")
89
+ self._loyalty_timeout = timeout
90
+ self._loyalty_retries = retries
91
+
92
+ def _init_loyalty_client(self) -> None:
93
+ """Initialize loyalty HTTP client (lazy initialization)"""
94
+ if self._loyalty_http_client is None:
95
+ self._loyalty_http_client = LoyaltyHttpClient(
96
+ client_key=self._loyalty_client_key,
97
+ client_secret=self._loyalty_client_secret,
98
+ base_url=self._loyalty_base_url,
99
+ timeout=self._loyalty_timeout,
100
+ retries=self._loyalty_retries,
101
+ )
62
102
 
63
103
  @property
64
104
  def campaigns(self) -> Campaigns:
@@ -129,3 +169,42 @@ class KiriMel:
129
169
  if self._webhooks is None:
130
170
  self._webhooks = Webhooks(self._http_client)
131
171
  return self._webhooks
172
+
173
+ @property
174
+ def email(self) -> Email:
175
+ """Get email resource client for transactional emails"""
176
+ if self._email is None:
177
+ self._email = Email(self._http_client)
178
+ return self._email
179
+
180
+ @property
181
+ def loyalty_customers(self) -> LoyaltyCustomers:
182
+ """Get loyalty customers resource client"""
183
+ self._init_loyalty_client()
184
+ if self._loyalty_customers is None:
185
+ self._loyalty_customers = LoyaltyCustomers(self._loyalty_http_client)
186
+ return self._loyalty_customers
187
+
188
+ @property
189
+ def loyalty_points(self) -> LoyaltyPoints:
190
+ """Get loyalty points resource client"""
191
+ self._init_loyalty_client()
192
+ if self._loyalty_points is None:
193
+ self._loyalty_points = LoyaltyPoints(self._loyalty_http_client)
194
+ return self._loyalty_points
195
+
196
+ @property
197
+ def loyalty_vouchers(self) -> LoyaltyVouchers:
198
+ """Get loyalty vouchers resource client"""
199
+ self._init_loyalty_client()
200
+ if self._loyalty_vouchers is None:
201
+ self._loyalty_vouchers = LoyaltyVouchers(self._loyalty_http_client)
202
+ return self._loyalty_vouchers
203
+
204
+ @property
205
+ def loyalty_wallet(self) -> LoyaltyWallet:
206
+ """Get loyalty wallet resource client"""
207
+ self._init_loyalty_client()
208
+ if self._loyalty_wallet is None:
209
+ self._loyalty_wallet = LoyaltyWallet(self._loyalty_http_client)
210
+ return self._loyalty_wallet
@@ -2,11 +2,18 @@
2
2
  KiriMel SDK Exception Classes
3
3
  """
4
4
 
5
+ from typing import Optional, Dict, Any
6
+
5
7
 
6
8
  class ApiException(Exception):
7
9
  """Base API exception"""
8
10
 
9
- def __init__(self, message: str, status_code: int = None, errors: dict = None):
11
+ def __init__(
12
+ self,
13
+ message: str,
14
+ status_code: Optional[int] = None,
15
+ errors: Optional[Dict[str, Any]] = None,
16
+ ):
10
17
  self.message = message
11
18
  self.status_code = status_code
12
19
  self.errors = errors
@@ -27,7 +34,13 @@ class RateLimitException(ApiException):
27
34
 
28
35
  error_type = "rate_limit_error"
29
36
 
30
- def __init__(self, message: str, status_code: int = None, errors: dict = None, retry_after: int = None):
37
+ def __init__(
38
+ self,
39
+ message: str,
40
+ status_code: Optional[int] = None,
41
+ errors: Optional[Dict[str, Any]] = None,
42
+ retry_after: Optional[int] = None,
43
+ ):
31
44
  super().__init__(message, status_code, errors)
32
45
  self.retry_after = retry_after
33
46
 
@@ -1,13 +1,19 @@
1
1
  """
2
2
  HTTP Client for KiriMel API
3
3
  """
4
+
4
5
  import os
5
6
  import time
6
7
  import logging
7
- from typing import Optional, Dict, Any, List
8
- import requests
8
+ from typing import Optional, Dict, Any, List, cast
9
+ import requests # type: ignore
9
10
 
10
- from .exceptions import ApiException, AuthenticationException, RateLimitException, ValidationException
11
+ from .exceptions import (
12
+ ApiException,
13
+ AuthenticationException,
14
+ RateLimitException,
15
+ ValidationException,
16
+ )
11
17
 
12
18
 
13
19
  class HttpClient:
@@ -53,6 +59,7 @@ class HttpClient:
53
59
  url = f"{self.base_url}/{path.lstrip('/')}"
54
60
  if params:
55
61
  import urllib.parse
62
+
56
63
  query_string = urllib.parse.urlencode(params)
57
64
  url = f"{url}?{query_string}"
58
65
  return url
@@ -88,7 +95,7 @@ class HttpClient:
88
95
  if response.status_code >= 400:
89
96
  self._handle_error(response, url, attempt)
90
97
 
91
- return response.json()
98
+ return cast(Dict[str, Any], response.json())
92
99
 
93
100
  def _build_headers(self) -> Dict[str, str]:
94
101
  """Build request headers"""