BACKGROUND PHOTO UPDATE ছবি বেছে নেওয়া/সেলফি তোলার পর ফর্ম না থামিয়ে গোপনে upload শুরু হবে। জমা দেওয়ার সময়ে অসম্পূর্ণ upload শেষ হওয়া পর্যন্ত অপেক্ষা করবে। একই ছবি একসঙ্গে দুইবার upload হবে না। সফল upload-এর key পুনরায় ব্যবহার করবে; ব্যর্থ হলে আবার চেষ্টা করবে। ফর্মে শুধু সংরক্ষণের অবস্থা দেখাবে, ছবির URL দেখাবে না। Admin-এর অনুমোদিত KYC preview একই panel-এ খুলবে, নতুন public photo tab নয়। কোনো URL প্রযুক্তিগতভাবে অদৃশ্য করা হয়নি: browser network-এ নিজের request দেখা যায়, কিন্তু URL একা কপি করে login ছাড়া ছবি খোলা যাবে না। অন্যের private ছবি access নিষিদ্ধ। ছবি ব্যবহারকারী নিজে ও অনুমোদিত admin দেখতে পারবেন। আপনার দেওয়া account/bucket values: R2 account: 0e7025162927b020b72b645d5d415a7b বর্তমান bucket নাম: ekannto-media নিচের MEDIA_BUCKET binding-এ ওই bucket নির্বাচন করা যাবে কেবল এটি private হলে। এটি Ekannto-এর public bucket হলে SBUT-এর NID-এর জন্য আলাদা private bucket ব্যবহার করুন। S3 access key/secret ও endpoint এই Worker integration-এ লাগে না; placeholder key বসানো হয়নি। এই update-এ নতুন SQL নেই। SQL009 আগে চালানো থাকলে আর চালাবেন না। SBUT PAY — KYC, OCR, R2 ও ২৫ অক্ষরের Order ID আপনার বর্তমান database-এ: ১. 009_KYC_R2_ORDER_SQL.txt-এর সম্পূর্ণ লেখা Supabase SQL Editor-এ একবার Run করুন। শেষের তিনটি ফলাফল true হতে হবে। আগে ঠিক করা registration/session SQL আবার লাগবে না। ২. নতুন Master ZIP Cloudflare Pages-এর একই stff project-এ নতুন deployment হিসেবে upload করুন। ZIP root-এ index.html, _worker.js, _routes.json, assets/ থাকতে হবে। ৩. Cloudflare R2-তে একটি PRIVATE bucket বানান, যেমন sbut-private-media। Public access / r2.dev public URL বন্ধ রাখুন। এই কাজ নিজে bucket তৈরি করে দেয়নি। ৪. Cloudflare → Workers & Pages → stff → Settings → Bindings → Add → R2 bucket: Variable name: MEDIA_BUCKET Bucket: আপনার private bucket নির্বাচন করুন। Production binding-এ দিন। ৫. একই Pages project-এর Settings → Variables and Secrets-এ Production values দিন: SUPABASE_URL = https://arsdjkylupklayojckbi.supabase.co SUPABASE_ANON_KEY = ঐ project-এর anon key SUPABASE_SERVICE_ROLE_KEY = ঐ project-এর service_role key (Encrypt/Secret হিসেবে) এগুলো Cloudflare Pages Worker-এর values, Supabase Edge Function-এর Secrets নয়। service_role browser/config/SQL/public ফাইলে দেবেন না, চ্যাটেও পাঠাবেন না। ৬. Bindings/secrets দেওয়ার পরে নতুন deployment/redeploy করুন। ৭. Safari-তে Reload → username/password দিয়ে login → KYC আবার খুলুন। আগে account হয়ে থাকলে Create Account করবেন না। ৮. NID-এর সামনে ও পেছনের স্পষ্ট ছবি, ক্যামেরা থেকে selfie দিন। “NID থেকে তথ্য পড়ুন” চাপলে প্রথমবার OCR engine/language download হবে। OCR বাংলা+ইংরেজি এই browser-এ চলে। কোনো OCR API key বা server OCR provider লাগে না। নিম্নমানের ছবিতে ভুল হতে পারে: প্রতিটি নাম/NID/জন্মতারিখ নিজে মিলিয়ে ঠিক করুন। OCR পরিচয়, NID বৈধতা, মুখ বা liveness অনুমোদন করে না। Admin review প্রয়োজন। ৯. সম্মতি → আলাদা ৬ সংখ্যার transaction PIN → জমা। সফল হলে login page-এ ফিরবে। KYC pending থাকবে; স্বয়ংক্রিয় approval নয়। ১০. Login-এর পরে profile avatar বা “প্রোফাইল ছবি” চাপলে ছবি পরিবর্তন করা যাবে। ছবির API ও privacy - Browser → একই domain-এর /api/media/upload → authenticated Pages Worker → private R2। - R2 binding থাকায় আলাদা S3 API access key প্রয়োজন নেই। - প্রত্যেক অনুরোধে Supabase user ও active wallet session যাচাই হয়। - NID, selfie, profile photo ৫ MB সীমা; ছবিগুলো JPEG-এ re-encode করে EXIF সরানো হয়। - অন্যের ছবি দেখা যাবে না; অনুমোদিত admin KYC ছবিগুলো দেখতে পারবেন। - R2 database metadata save ব্যর্থ হলে সদ্য upload হওয়া object মুছে দেওয়া হয়। - পুনরায় চেষ্টা একই পেজে cached uploaded keys ব্যবহার করে। Reload করলে আবার upload লাগতে পারে। - অসম্পূর্ণ/বাতিল KYC-এর orphan object retention ও নিয়মিত cleanup operator-কে পরিচালনা করতে হবে। - আগে Supabase Storage-এ থাকা ছবি R2-তে স্বয়ংক্রিয় migrate হয়নি; পুরোনো admin preview বজায় আছে। Order ID - ২৫ অক্ষর: SB + ২৩ সংখ্যা। উদাহরণ: SB00000000000000000000001 - একই database-এর global sequence ও UNIQUE constraint থেকে তৈরি, browser থেকে নয়। - Send/cash-in/cash-out/merchant funding/admin credit/debit/bonus/refundসহ sbut_transactions-এর প্রতিটি রেকর্ডে আলাদা Order ID mapping হয়। - recharge ও money-request table থাকলে সেগুলোতেও একই global series থেকে ID হয়। - আগে থাকা রেকর্ডেও ID backfill হয়। দুই পক্ষ একই লেনদেনের একই Order ID দেখবেন। - নতুন লেনদেনে নতুন ID। Retry-তে একই ledger record হলে একই ID। - Rollback/concurrent request-এর কারণে sequence-এ gap হতে পারে; commit-এর সময়ের সঙ্গে হুবহু ক্রমের নিশ্চয়তা নয়। ID অনুমান করা গেলেও তা login/access নয়। - ভিন্ন/আলাদা database clone-এর মধ্যে global uniqueness দাবি করা হচ্ছে না। - Internal transaction UUID, deposit reference ও হিসাব অপরিবর্তিত থাকে। - History ও receipt/download-এ Order ID পাওয়া যাবে, UUID-ও থাকবে। যদি error থাকে - ফর্মের নিচে এবার কোন ধাপ বাকি তা লেখা থাকবে; input ফাঁকা করে দেওয়া হবে না। - R2_SETUP_REQUIRED: Cloudflare binding/secrets বা redeployment বাকি। - SQL_009_REQUIRED: নতুন SQL চলেনি বা cache পুরোনো। - SESSION_LOCKED: আবার login করুন। - INVALID_IDENTITY: ১০/১৩/১৭ সংখ্যার NID এবং ১৮+ বয়সের সঠিক জন্মতারিখ। - OCR_FAILED: স্পষ্ট ছবি দিন বা হাতে লিখুন। পরীক্ষা ও সীমা - স্থানীয় SQL semantics: KYC, আলাদা PIN, private permissions, ১০,০০০ আলাদা Order ID পরীক্ষা হয়েছে। - আসল Tesseract engine-এ কৃত্রিম নাম/তারিখ/NID নমুনা পড়া পরীক্ষা হয়েছে। - R2 API authentication/authorization/cleanup mocked test; আপনার bucket-এ live পরীক্ষা হয়নি। - বাস্তব iPhone camera/OCR quality, live R2 upload ও পূর্ণ KYC-to-admin approval পরীক্ষা বাকি। - OTP, OCR বা storage setup-কে ব্যবসা/আর্থিক অনুমোদন হিসেবে গণ্য করবেন না।