iyzico / iyzipay-python

iyzipay api python client
MIT License
87 stars 35 forks source link

Bugs I experienced & Concept (FastAPI, MongoDB, React) #91

Open OrcunSamiTandogan opened 8 months ago

OrcunSamiTandogan commented 8 months ago

Hi, I want to understand the concept of the Iyzico and I have experienced some issue and I hope you can help me out. 1- SSL: I had to use this to overcome SSL cerficaition problem. Terminal code:

/python3.10/ssl.py", line 1342, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1007)

To overcome this issue, I temporarily did this:

#%% SSL Part 
import ssl 
import urllib.request

ssl._create_default_https_context = ssl._create_unverified_context
urllib.request.urlopen("https://google.com").read()

However, it is not valid approach for production environment.

2- Working pipeline of Iyzico and models. To understand better I created a visualization: Screenshot 2024-03-02 at 19 26 00

So what we need to create payment: payment_card, buyer, address and basket_items. These and options creates inside of "iyzipay.Payment().create". However, I still did not get how the process goes. So I also created visual for that: Screenshot 2024-03-02 at 19 27 20 In samples folder, there is no a simple way of usage I can find. Please help me to find where is this simple process: 1- User comes, selects item, add basket. (So far, it is easy) 2- User goes to checkout, enter credit card info, clicks PayNow(we are assuming), then this information goes to Iyzico. After this, including this, I am lost. Which parameters I useful? How do I check return of Iyzico? I believe we need to confirm some tokens. There are files in the samples folder but there is not any directive guide how to use them. Problems I am having is lack of direction actually. Iyzico needs Bin number, why? How do we use them? What is working pipeline of 3D and non-3D secure? What are their differences in code-wise? Where the conversation IDs are created? Can we generate our unique conversation IDs and baskedIDs? Is there any format we should be take care of for not get fail by Iyzico?


To able to mimic sample code in first page, XXX, I did this:


def r_(barcode, report = True):
    if report == True:
        print("Current Barcode:", str(barcode))  

@router.post("/api/pay", tags=["payments"])
async def create_payment(payment_request: PaymentCardModel, 
                         current_user: dict = Depends(get_current_user)):
    print("-----------------")
    print("Current User:", current_user["email_DB"]) 
    try: 
        r_(1)
        current_payment_request = payment_request.model_dump() 
        print("Current Payment Request Dict:", current_payment_request)  
        r_(2)
        # Assuming `test_Buyer`, `test_Address`, and `test_BasketItem` are already populated correctly
        # Convert the single basket item to a list for the basketItems field
        basket_items_list = [test_BasketItem.model_dump()]

        # Structure merged_data with the correct field names and data
        merged_data = {
            "paymentCard":     test_card.model_dump(),  # Directly assigning the dictionary of payment card details
            "buyer":           test_Buyer.model_dump(),  # Assuming this returns a dictionary of buyer details
            "shippingAddress": test_Address.model_dump(),  # Assuming this returns a dictionary of address details
            "billingAddress":  test_Address.model_dump(),  # Re-using the same address for billing; adjust as needed
            "basketItems":     basket_items_list,  # Assigning the list of basket item dictionaries
            # Add any other missing fields required by PaymentRequestModel here
        }

        request = PaymentRequestModel(**merged_data).model_dump() 
        print("Current request:", request) 
        r_(3)
        payment_response = iyzipay.Payment().create(request, options)
        print("Payment Response:", payment_response)
        r_(4)
        payment_response = json.loads(payment_response.read().decode('utf-8'))
        print("Payment Response:", payment_response)

        r_(5)
        if payment_response.get("status") == "success":
            r_(6)
            # Handle successful payment
            return {"status": "Success", "message": "Payment successful", "paymentResponse": payment_response}
        else:
            r_(7)
            # Handle payment failure
            return {"status": "Failure", "message": "Payment failed", "paymentResponse": payment_response}
    except HTTPException as e:
        return {"status": "Failure", "message": "Payment failed", "paymentResponse": e.detail} 

and I used these models:


class PaymentCardModel(BaseModel):
    cardHolderName: Optional[str] = 'John Doe'
    cardNumber: Optional[str] = '5528790000000008'
    expireMonth: Optional[str] = '12'
    expireYear: Optional[str] = '2030'
    cvc: Optional[str] = '123'
    registerCard: Optional[int] = 0

class BuyerModel(BaseModel):
    id: Optional[str] = 'BY789'
    name: Optional[str] = 'John'
    surname: Optional[str] = 'Doe'
    gsmNumber: Optional[str] = '+905350000000'
    email: Optional[str] = 'email@email.com'
    identityNumber: Optional[str] = '74300864791'
    lastLoginDate: Optional[str] = '2015-10-05 12:43:35'
    registrationDate: Optional[str] = '2013-04-21 15:12:09'
    registrationAddress: Optional[str] = 'Nidakule Göztepe, Merdivenköy Mah. Bora Sok. No:1'
    ip: Optional[str] = '85.34.78.112'
    city: Optional[str] = 'Istanbul'
    country: Optional[str] = 'Turkey'
    zipCode: Optional[str] = '34732'

class AddressModel(BaseModel):
    contactName: Optional[str] = 'Jane Doe'
    city: Optional[str] = 'Istanbul'
    country: Optional[str] = 'Turkey'
    address: Optional[str] = 'Nidakule Göztepe, Merdivenköy Mah. Bora Sok. No:1'
    zipCode: Optional[str] = '34732'

class BasketItemModel(BaseModel):
    id: Optional[str] = 'Gold101'
    name: Optional[str] = 'Gold'
    category1: Optional[str] = 'Models'
    category2: Optional[str] = 'Membership'
    itemType: Optional[str] = 'PHYSICAL'
    price: Optional[str] = '20.0'

class PaymentRequestModel(BaseModel):
    locale: Optional[str] = 'tr'
    conversationId: Optional[str] = '123456789'
    price: Optional[str] = '20.0'
    paidPrice: Optional[str] = '24.0'
    currency: Optional[str] = 'TRY'
    installment: Optional[int] = 1
    basketId: Optional[str] = 'B67832'
    paymentChannel: Optional[str] = 'WEB'
    paymentGroup: Optional[str] = 'PRODUCT'
    paymentCard: PaymentCardModel
    buyer: BuyerModel
    shippingAddress: AddressModel
    billingAddress: AddressModel
    basketItems: list[BasketItemModel]

However, result was:

Current User: contact@XXXXXX.com
Current Barcode: 1
Current Payment Request Dict: {'cardHolderName': 'John Doe', 'cardNumber': '5528790000000008', 'expireMonth': '12', 'expireYear': '2030', 'cvc': '123', 'registerCard': 0}
Current Barcode: 2
Current request: {'locale': 'tr', 'conversationId': '123456789', 'price': '20.0', 'paidPrice': '24.0', 'currency': 'TRY', 'installment': 1, 'basketId': 'B67832', 'paymentChannel': 'WEB', 'paymentGroup': 'PRODUCT', 'paymentCard': {'cardHolderName': 'John Doe', 'cardNumber': '5528790000000008', 'expireMonth': '12', 'expireYear': '2030', 'cvc': '123', 'registerCard': 0}, 'buyer': {'id': 'BY789', 'name': 'John', 'surname': 'Doe', 'gsmNumber': '+905350000000', 'email': 'email@email.com', 'identityNumber': '74300864791', 'lastLoginDate': '2015-10-05 12:43:35', 'registrationDate': '2013-04-21 15:12:09', 'registrationAddress': 'Nidakule Göztepe, Merdivenköy Mah. Bora Sok. No:1', 'ip': '85.34.78.112', 'city': 'Istanbul', 'country': 'Turkey', 'zipCode': '34732'}, 'shippingAddress': {'contactName': 'Jane Doe', 'city': 'Istanbul', 'country': 'Turkey', 'address': 'Nidakule Göztepe, Merdivenköy Mah. Bora Sok. No:1', 'zipCode': '34732'}, 'billingAddress': {'contactName': 'Jane Doe', 'city': 'Istanbul', 'country': 'Turkey', 'address': 'Nidakule Göztepe, Merdivenköy Mah. Bora Sok. No:1', 'zipCode': '34732'}, 'basketItems': [{'id': 'Gold101', 'name': 'Gold', 'category1': 'Models', 'category2': 'Membership', 'itemType': 'PHYSICAL', 'price': '20.0'}]}
Current Barcode: 3
Payment Response: <http.client.HTTPResponse object at 0x107479ba0>
Current Barcode: 4
Payment Response: {'status': 'failure', 'errorCode': '1000', 'errorMessage': 'Geçersiz imza', 'locale': 'tr', 'systemTime': 1709401195452, 'conversationId': '123456789'}
Current Barcode: 5
Current Barcode: 7

Do not mind the current_user because it is about auth. system of user. That part works. But I send information directly from test data as you can see. This is so weird: "Payment Response: {'status': 'failure', 'errorCode': '1000', 'errorMessage': 'Geçersiz imza', 'locale': 'tr', 'systemTime': 1709401195452, 'conversationId': '123456789'}"

I tried to be specific as much as I can. You can check my current code: https://github.com/OrcunSamiTandogan/paymentSystem I will be so glad, if you can help me out. Best!

Systems: python 3.10.11 Macbook M2. VScode. Backend: FastAPI Frontend: React Database: MongoDB (not integrated yet)


annotated-types==0.6.0 anyio==4.2.0 bcrypt==4.1.2 cachetools==5.3.2 certifi==2024.2.2 cffi==1.16.0 charset-normalizer==3.3.2 click==8.1.7 cryptography==42.0.3 dnspython==2.6.0 exceptiongroup==1.2.0 fastapi==0.109.2 google-auth==2.28.0 h11==0.14.0 httpcore==1.0.3 httpx==0.26.0 idna==3.6 jwt==1.3.1 passlib==1.7.4 pyasn1==0.5.1 pyasn1-modules==0.3.0 pycparser==2.21 pydantic==2.6.1 pydantic_core==2.16.2 PyJWT==2.8.0 pymongo==4.6.1 python-multipart==0.0.9 requests==2.31.0 rsa==4.9 sniffio==1.3.0 starlette==0.36.3 typing==3.7.4.3 typing_extensions==4.9.0 urllib3==1.26.7 uuid==1.30 uvicorn==0.27.1 numpy boto3==1.28.57 botocore==1.31.57 python-dotenv services iyzipay certifi