> ## Documentation Index
> Fetch the complete documentation index at: https://docs.classatlas.ge/llms.txt
> Use this file to discover all available pages before exploring further.

# პირადი ნომრით გადახდის პროტოკოლი

> მისამართი, რომელსაც პირადი ნომრით გადახდის ყველა პროვაიდერი მიმართავს, ClassAtlas-ის რეალიზაციით: შემოწმებების თანმიმდევრობა, IP სია, HASH_CODE ხელმოწერა, debt, verify, pay და ping მოთხოვნები და სტატუს კოდები.

პროვაიდერი თითო სკოლის ერთ მისამართს HTTPS `GET` მოთხოვნით მიმართავს. ClassAtlas ყოველთვის XML-ით და HTTP 200-ით პასუხობს, შედეგს კი `status` ელემენტის `code` ამბობს. პროტოკოლი iPay-ის სპეციფიკაციიდან დაიწყო (`payment-gateway.doc`, ვერსია 1.03) და ახლა მას ყველა პროვაიდერი იყენებს (ADR 0074). ეს გვერდი აღწერს, როგორ ახორციელებს მას ClassAtlas.

## მისამართი

| პუნქტი | მნიშვნელობა |
| - | - |
| მისამართი | სკოლის საკუთარი ქვედომენი, თითო პროვაიდერზე ცალკე გზით: iPay-ისთვის `https://{school}.classatlas.ge/ipay`, TBC Pay-ისთვის `https://{school}.classatlas.ge/tbcpay` |
| მეთოდი | `GET`, პარამეტრები query string-ში |
| პასუხი | XML, `Content-Type: application/xml; charset=UTF-8`, ყოველთვის HTTP 200 |
| სესია | არ არის: არც cookie, არც სესია, არც CSRF ტოკენი |
| ლიმიტი | წუთში 300 მოთხოვნა თითო სკოლაზე |

ამ გვერდის მაგალითები iPay-ის მისამართს იყენებს. TBC Pay ზუსტად იგივე მოთხოვნებს საკუთარ მისამართზე აგზავნის.

> [!NOTE]
>
> **დაგეგმილია** ([CA-1147](https://bakidevelopment.atlassian.net/browse/CA-1147)): TBC Pay-ის მისამართი ჯერ არ პასუხობს.

## შემოწმებები თანმიმდევრობით

ყოველი მოთხოვნა ამ შემოწმებებს ამ თანმიმდევრობით გადის. პირველი, რომელიც ვერ გაივლის, განსაზღვრავს პასუხს.

| # | შემოწმება | კოდი წარუმატებლობისას |
| - | - | - |
| 1 | გამომძახებლის IP მისამართი დაშვებულთა სიაშია | 1 |
| 2 | ქვედომენი სკოლას ეკუთვნის | 1 |
| 3 | სკოლა ლიმიტს არ აჭარბებს | 99, `retry="true"`-ით |
| 4 | სკოლას ეს პროვაიდერი ჩართული და ბოლომდე მორგებული აქვს | 1 |
| 5 | `USERNAME` და `PASSWORD` სკოლისას ემთხვევა | 2 |
| 6 | `HASH_CODE` ემთხვევა | 3 |
| 7 | პარამეტრები ოპერაციისთვის სწორია | 4, 10, 7 ან 5 |
| 8 | თავად ოპერაცია წარმატებით სრულდება | 6, 9, 8 ან 99 |

IP შემოწმება სკოლის განსაზღვრამდე ხდება, ასე რომ უცხო გამომძახებელი სკოლის მონაცემთა ბაზას ვერასდროს ხსნის და მის ლიმიტს ვერ ხარჯავს. ავთენტიფიკაცია ამის შემდეგ ხდება, რადგან თითო სკოლის წვდომის მონაცემები მის საკუთარ ბაზაშია (ADR 0029).

> [!NOTE]
>
> ამ მისამართზე ნებისმიერ მოულოდნელ შეცდომას კოდი 99 პასუხობს და არასდროს HTML შეცდომის გვერდი.

## ავთენტიფიკაცია

### დაშვებული IP-ები

მხოლოდ პროვაიდერების სერვერები მიიღება. პროვაიდერი ClassAtlas-ს აწვდის მისამართებს, საიდანაც მიმართავს, და ერთი სია ყველა სკოლასა და ყველა პროვაიდერს ემსახურება.

* შემოწმება იყენებს მისამართს, საიდანაც კავშირი მოვიდა, და არა `X-Forwarded-For`-ს, რომლის გაყალბებაც ნებისმიერ გამომძახებელს შეუძლია (ADR 0028).
* სიაში არსებულ მისამართსაც სჭირდება ამ პროვაიდერთან სკოლის წვდომის მონაცემები და საიდუმლო გასაღები.

### წვდომის მონაცემები

თითო სკოლას თითოეულ პროვაიდერთან თავისი `USERNAME`, `PASSWORD` და საიდუმლო გასაღები აქვს, დაშიფრულად შენახული სკოლის ბაზაში. მომხმარებელიც და პაროლიც პასუხამდე მოწმდება, მუდმივ დროში, ასე რომ არასწორ მომხმარებელს და არასწორ პაროლს ერთნაირი დრო სჭირდება.

### HASH\_CODE

`HASH_CODE` არის ყველა პარამეტრის მნიშვნელობის MD5, იმ თანმიმდევრობით, რომლითაც პარამეტრები მოდის, ბოლოს სკოლის საიდუმლო გასაღებით, დიდი ასოებით hex ფორმატში.

```text theme={null}
HASH_CODE = UPPER( MD5( v1 + v2 + … + vN + SECRET_KEY ) )
```

* თავად `HASH_CODE` გამოტოვებულია, მოთხოვნაში სადაც არ უნდა იყოს.
* მნიშვნელობები URL-დეკოდირებული სახით ერთდება, გამყოფის გარეშე.
* თანმიმდევრობა მოსვლის თანმიმდევრობაა და არა ფიქსირებული სია. თუ პროვაიდერი ახალ პარამეტრს დაამატებს, ჰეში მაინც გაივლის (ADR 0026).

მაგალითი `pay`-სთვის, სატესტო მნიშვნელობებით და საიდუმლო გასაღებით `DemoSecretKey123`:

```text theme={null}
MD5 input:  payipay_demoDemo2026passtuition_fee0100101234525000mbankTEST0000000001DemoSecretKey123
HASH_CODE:  BA3DE786E37DDEDBC642763F251E36A8
```

> [!NOTE]
>
> დაუდასტურებელია, პროვაიდერი დეკოდირებულ მნიშვნელობას ჰეშავს თუ ნედლს. ეს მხოლოდ `EXTRA_INFO`-ს ეხება, ერთადერთ პარამეტრს, რომელშიც კოდირების საჭიროების მქონე სიმბოლოები შეიძლება მოხვდეს, და iPay-მა დაადასტურა, რომ მასში არაფერს აგზავნის. საკითხს სკოლის ჩართვამდე პროვაიდერის სატესტო გაშვება წყვეტს.

## პარამეტრები

პარამეტრი ჩამოთვლილ ოპერაციებში სავალდებულოა, თუ არასავალდებულოდ არ არის აღნიშნული.

| პარამეტრი | ოპერაციები | აღწერა |
| - | - | - |
| `OP` | ყველა | `debt`, `verify`, `pay` ან `ping` |
| `USERNAME` | ყველა | სკოლის მომხმარებელი |
| `PASSWORD` | ყველა | სკოლის პაროლი |
| `SERVICE_ID` | `debt`, `verify`, `pay` | სკოლის ერთ-ერთი სერვისი. იხ. [სერვისები, ანგარიშსწორების ანგარიშები და მოსწავლის ძიება](https://docs.classatlas.ge/payments/lookup-payments/services-and-students.md) |
| `CUSTOMER_ID` | `debt`, `verify`, `pay` | მოსწავლის პირადი ნომერი |
| `PAY_AMOUNT` | `verify`, `pay` | თანხა თეთრებში, მთელი რიცხვი 1-დან 99 999 999-მდე (999 999,99 ₾) |
| `PAY_SRC` | `pay`, არასავალდებულო | არხი. იხ. [არხები](https://docs.classatlas.ge/payments/lookup-payments/overview.md#არხები) |
| `PAYMENT_ID` | `pay` | პროვაიდერის ID გადახდისთვის, მაქსიმუმ 255 სიმბოლო |
| `EXTRA_INFO` | `pay`, არასავალდებულო | ინახება ტრანზაქციასთან მტკიცებულებად. iPay მასში არაფერს აგზავნის |
| `HASH_CODE` | ყველა | ხელმოწერა |

`PAY_AMOUNT`-ის ზედა ზღვარი ყველაზე დიდი თანხაა, რომელსაც ტრანზაქცია იტევს.

## პასუხები

ყოველი პასუხი ერთი `pay-response` დოკუმენტია: `status` კოდით და ტექსტით, `timestamp` Unix წამებში და, წარმატებისას, ოპერაციის შედეგი. უარს `retry` ატრიბუტიც ახლავს.

```xml უარი theme={null}
<?xml version="1.0" encoding="UTF-8"?>
<pay-response>
  <status code="6" retry="false">Customer does not exist</status>
  <timestamp>1793704705</timestamp>
</pay-response>
```

`retry` ყოველთვის `false`-ია, გარდა იმ შემთხვევისა, როცა სკოლის ლიმიტი ამოიწურა. პროვაიდერისთვის იმის თქმა, რომ უარყოფილი გადახდა შეიძლება გამეორდეს, როცა არ შეიძლება, ორმაგ გადახდამდე მიდის.

## ოპერაციები

### debt

აბრუნებს, რამდენი მართებს მოსწავლეს და რისგან შედგება დავალიანება.

```text მოთხოვნა theme={null}
https://demo.classatlas.ge/ipay?OP=debt&USERNAME=ipay_demo&PASSWORD=Demo2026pass&SERVICE_ID=tuition_fee&CUSTOMER_ID=01001012345&HASH_CODE=43530F79CCA1BACE518C41CD4CEAD4CE
```

```xml პასუხი theme={null}
<?xml version="1.0" encoding="UTF-8"?>
<pay-response>
  <status code="0">OK</status>
  <timestamp>1793704705</timestamp>
  <debt>25000</debt>
  <additional-info>
    <parameter name="first_name">ნინო</parameter>
    <parameter name="last_name">ბერიძე</parameter>
    <parameter name="charge_1">ოქტ. 2026 — I ჯგუფი — 150.00 ₾</parameter>
    <parameter name="charge_2">ნოე. 2026 — I ჯგუფი — 150.00 ₾</parameter>
    <parameter name="balance">ბალანსი — 50.00 ₾</parameter>
  </additional-info>
</pay-response>
```

`debt` არის მოსწავლის დარიცხვებს გამოკლებული ბალანსი, თეთრებში და არასდროს ნულზე ნაკლები. ის იმავე ბალანსის ხედიდან იკითხება, რომელსაც ClassAtlas-ის პანელი იყენებს, ასე რომ ბანკი და სკოლა ყოველთვის ერთსა და იმავე რიცხვს ხედავენ.

`additional-info` გადამხდელისთვისაა. მნიშვნელობები მთელ ხაზს შეიცავს, სახელები კი მხოლოდ გასაღებებია, რადგან სპეციფიკაცია არ ამბობს, აჩვენებს თუ არა პროვაიდერი სახელებს. ტექსტი ყოველთვის ქართულია, აპლიკაციის ენის მიუხედავად.

| `name` | როდის იგზავნება | მნიშვნელობა |
| - | - | - |
| `first_name`, `last_name` | ყოველთვის | მოსწავლის სახელი და გვარი |
| `charge_1` … `charge_N` | თითო გადაუხდელ დარიცხვაზე, ძველიდან ახლისკენ | დარიცხვის სათაური და თანხა: `ოქტ. 2026 — I ჯგუფი — 150.00 ₾`. ყოველთვიური დარიცხვის სათაური შემოკლებული თვე, წელი და ჯგუფია; ინვოისს საკუთარი სათაური რჩება |
| `balance` | სკოლას მოსწავლისთვის ბალანსი აქვს | ბალანსი — თანხა ₾ |

სანამ მოსწავლეს დავალიანება აქვს, `balance` დარიცხვებთან უკვე გაქვითული ბალანსია, ასე რომ დარიცხვებს გამოკლებული ბალანსი `debt`-ს უდრის. როცა არაფერი მართებს, ეს მისი ბალანსია, რათა მშობელმა განასხვავოს „ანგარიში გასწორებულია" და „სკოლა ჩემს შვილს არ იცნობს".

### verify

პასუხობს, შეიძლება თუ არა გადახდა. არაფერს წერს, რადგან ოპერატორთან გადახდისას მანამ იგზავნება, სანამ კლიენტი თანხას გადასცემს.

უარს ამბობს მხოლოდ მოსწავლეზე, რომელიც სკოლიდან დავალიანების გარეშე წავიდა (კოდი 9). თანხის ზედა ზღვარი არ არის: მშობელს შეუძლია იმაზე მეტი გადაიხადოს, ვიდრე მართებს.

```xml პასუხი theme={null}
<?xml version="1.0" encoding="UTF-8"?>
<pay-response>
  <status code="0">OK</status>
  <timestamp>1793704705</timestamp>
</pay-response>
```

### pay

წერს გადახდას და აბრუნებს მის ქვითრის ნომერს.

```text მოთხოვნა theme={null}
https://demo.classatlas.ge/ipay?OP=pay&USERNAME=ipay_demo&PASSWORD=Demo2026pass&SERVICE_ID=tuition_fee&CUSTOMER_ID=01001012345&PAY_AMOUNT=25000&PAY_SRC=mbank&PAYMENT_ID=TEST0000000001&HASH_CODE=BA3DE786E37DDEDBC642763F251E36A8
```

```xml პასუხი theme={null}
<?xml version="1.0" encoding="UTF-8"?>
<pay-response>
  <status code="0">OK</status>
  <timestamp>1793704705</timestamp>
  <receipt-id>48213</receipt-id>
</pay-response>
```

1. `verify`-ის შემოწმება თავიდან სრულდება, რადგან ოპერატორის დავალება შეიძლება მისი შექმნიდან დიდი ხნის შემდეგ გადაიხადონ.
2. მოსწავლისთვის ერთი ტრანზაქცია იწერება. `receipt-id` მისი ID-ია ClassAtlas-ში.
3. დანარჩენი ყველაფერი, დარიცხვების დაფარვიდან ოჯახისთვის შეტყობინებამდე, ამ ტრანზაქციიდან გამომდინარეობს. იხ. [როგორ წერს ClassAtlas პირადი ნომრით გადახდას](https://docs.classatlas.ge/payments/lookup-payments/recording-payments.md).

`PAYMENT_ID` თითო პროვაიდერისა და არხისთვის ერთხელ იწერება. იმავე არხიდან იმავე ID-ის განმეორებით მოსვლას კოდი 8 პასუხობს და არაფერი იწერება. ამიტომ დაკარგული პასუხის შემდეგ განმეორებული მოთხოვნა კოდ 8-ს იღებს და არა თავდაპირველ `receipt-id`-ს. iPay-მა დაადასტურა, რომ კოდები 8 და 18 მათთვის ერთსა და იმავეს ნიშნავს.

გადახდის გაუქმების ოპერაცია არ არსებობს.

### ping

პასუხობს, შეუძლია თუ არა სკოლას ახლა გადახდების მიღება. ის ავთენტიფიკაციის ყველა შემოწმებას გადის, შემდეგ კი კითხულობს მოსწავლეების ბალანსის ხედს, რომელზეც ფულთან დაკავშირებული ყველა ოპერაციაა დამოკიდებული.

```text მოთხოვნა theme={null}
https://demo.classatlas.ge/ipay?OP=ping&USERNAME=ipay_demo&PASSWORD=Demo2026pass&HASH_CODE=AFAA13AAD6730A86D47EDB59D6882CD1
```

გაუმართავი ბაზა კოდ 99-ს აბრუნებს, რაზეც პროვაიდერი განგაშს რთავს. შეჩერებული ან ბოლომდე მოურგებელი სკოლა კოდ 1-ს აბრუნებს.

## სტატუს კოდები

| კოდი | ტექსტი | ClassAtlas ამ კოდს აბრუნებს, როცა |
| - | - | - |
| 0 | OK | ოპერაცია წარმატებით შესრულდა |
| 1 | Access is restricted | IP მისამართი არ არის დაშვებული, ქვედომენს სკოლა არ აქვს, ან სკოლას ეს პროვაიდერი გამორთული ან ბოლომდე მოურგებელი აქვს |
| 2 | Username or password is invalid | წვდომის მონაცემები სკოლისას არ ემთხვევა |
| 3 | Hash code is invalid | `HASH_CODE` არ ემთხვევა |
| 4 | Required parameter is missing | სავალდებულო პარამეტრი აკლია. ეს ყველა სხვა პარამეტრის შეცდომას წინ უსწრებს |
| 5 | Parameter value is invalid | მნიშვნელობა ვერ იკითხება, მაგალითად უცნობი `OP` ან თანხა, რომელიც მთელი რიცხვი არ არის |
| 6 | Customer does not exist | ამ სერვისზე ამ პირადი ნომრით მოსწავლე არ არის, ან მოსწავლე დავალიანების გარეშე წავიდა |
| 7 | Payment amount is invalid | თანხა 1 თეთრზე ნაკლები ან 999 999,99 ₾-ზე მეტია |
| 8 | Payment id is not unique | ეს `PAYMENT_ID` ამ არხზე უკვე ჩაწერილია |
| 9 | Payment is not possible | მოსწავლე სკოლიდან დავალიანების გარეშე წავიდა |
| 10 | Service does not exist | `SERVICE_ID` სკოლის სერვისებში არ არის |
| 99 | General error | ლიმიტი ამოიწურა (`retry="true"`), ან მოულოდნელი შეცდომა (`retry="false"`) |

პარამეტრების შეცდომები ფიქსირებული თანმიმდევრობით ბრუნდება: ჯერ დაკარგული პარამეტრი (4), შემდეგ უცნობი სერვისი (10), შემდეგ საზღვრებს გარეთ თანხა (7), შემდეგ ნებისმიერი სხვა არასწორი მნიშვნელობა (5). კოდი 18 სპეციფიკაციაში არსებობს, მაგრამ ClassAtlas მას არასდროს აგზავნის.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.