# Python 3, standard library only. API key is a server environment variable. import os, json, time, urllib.request, urllib.error, urllib.parse BASE=os.environ.get('TURBOPULSA_API_URL','https://api.turbopulsa.com/sandbox/v1').rstrip('/') u=urllib.parse.urlparse(BASE) if not (u.scheme=='https' and u.hostname=='api.turbopulsa.com') and not (os.environ.get('TURBOPULSA_FIXTURE')=='1' and u.scheme=='http' and u.hostname in ['127.0.0.1','localhost']): raise ValueError('Use branded HTTPS API; HTTP is fixture-only.') def api(path, method='GET', body=None, uuid=None): headers={'Authorization':'Bearer '+os.environ['TURBOPULSA_API_KEY'],'Accept':'application/json'} if uuid: headers.update({'Idempotency-Key':uuid,'Content-Type':'application/json'}) request=urllib.request.Request(BASE+path, method=method, headers=headers, data=json.dumps(body).encode() if body is not None else None) try: with urllib.request.urlopen(request, timeout=15) as response: return json.load(response) except urllib.error.HTTPError as error: data=json.load(error) raise ValueError(data.get('error',{}).get('code','request_failed')) from None # A confirmation timeout must read the same order; callers never generate another purchase. def main(): reference=os.environ['TURBOPULSA_CLIENT_REFERENCE']; quote_uuid=os.environ['TURBOPULSA_QUOTE_UUID']; confirm_uuid=os.environ['TURBOPULSA_CONFIRM_UUID'] postpaid=os.environ.get('TURBOPULSA_KIND')=='postpaid' catalog=api(('/postpaid/catalog' if postpaid else '/catalog')+'?per_page=50') code=os.environ.get('TURBOPULSA_PRODUCT_CODE') or catalog['data'][0]['code'] destination=json.loads(os.environ.get('TURBOPULSA_DESTINATION_JSON','{"phone":"081200000000"}')) body={'product_code':code,'client_reference':reference} body.update({'customer_no':os.environ.get('TURBOPULSA_CUSTOMER_NO','001234567890')} if postpaid else {'destination':destination}) prepared=api('/postpaid/inquiries' if postpaid else '/quotes','POST',body,quote_uuid) for attempt in range(5): if prepared.get('data') or not postpaid or not prepared.get('inquiry',{}).get('id'): break time.sleep(1); prepared=api('/postpaid/inquiries/'+prepared['inquiry']['id']) quote=prepared.get('data') if not quote or not quote.get('id'): raise ValueError('inquiry_pending_check_same_reference') print(json.dumps({k:quote.get(k) for k in ['id','name','price','currency','status']})) if input('Periksa tujuan lokal dan total. Ketik BAYAR untuk konfirmasi: ').strip()!='BAYAR': return try: result=api('/orders/'+quote['id']+'/confirm','POST',{'confirmation_token':quote['confirmation_token']},confirm_uuid) except (TimeoutError,urllib.error.URLError): result=api('/orders/'+quote['id']) current=api('/orders/'+result['data']['id'])['data'] print(json.dumps({k:current.get(k) for k in ['id','status','charged','currency']})) if __name__=='__main__': try: main() except (ValueError,KeyError,TimeoutError,urllib.error.URLError): print('Request belum selesai. Periksa referensi yang sama; jangan buat pembelian kedua.') raise SystemExit(1)