mirror of
https://wiilab.wiimart.org/wiimart/WiiSOAP
synced 2025-09-03 20:11:14 +02:00
739 lines
47 KiB
Go
739 lines
47 KiB
Go
// Copyright (C) 2018-2020 CornierKhan1
|
|
//
|
|
// WiiSOAP is SOAP Server Software, designed specifically to handle Wii Shop Channel SOAP.
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published
|
|
// by the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see http://www.gnu.org/licenses/.
|
|
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/md5"
|
|
"encoding/binary"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"html"
|
|
"log"
|
|
"math"
|
|
"math/rand"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
v1Ticket "github.com/OpenShopChannel/V1TicketGenerator"
|
|
"github.com/antchfx/xmlquery"
|
|
"github.com/wii-tools/wadlib"
|
|
)
|
|
|
|
const (
|
|
QueryOwnedTitles = `SELECT owned_titles.title_id
|
|
FROM owned_titles
|
|
WHERE owned_titles.account_id = $1`
|
|
|
|
QueryOwnedServiceTitles = `SELECT service_titles.reference_id, owned_titles.date_purchased, service_titles.item_id
|
|
FROM service_titles, owned_titles
|
|
WHERE service_titles.item_id = owned_titles.item_id
|
|
AND service_titles.title_id = $1
|
|
AND owned_titles.account_id = $2`
|
|
|
|
AssociateTicketStatement = `INSERT INTO owned_titles (account_id, title_id, version, item_id, date_purchased)
|
|
VALUES ($1, $2, $3, $4, $5)
|
|
ON CONFLICT (accound_id) DO NOTHING`
|
|
|
|
AssociatePointsStatement = `INSERT INTO public.userbase (device_id, device_token, device_token_hashed, account_id, region, serial_number, points)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
ON CONFLICT (device_id) DO UPDATE SET points = $7`
|
|
|
|
QueryPointsStatement = `SELECT points from public.userbase WHERE device_id = $1`
|
|
|
|
AssociateGiftedTitleStatement = `INSERT INTO public.gifted_titles (title_id, trans_id, friend_code)
|
|
VALUES ($1, $2, $3)`
|
|
|
|
QueryGiftedTitleStatement = `SELECT trans_id FROM public.gifted_titles`
|
|
|
|
RemoveGiftedTitleStatement = `DELETE FROM public.gifted_titles
|
|
WHERE title_id = $1 AND trans_id = $2`
|
|
// SharedBalanceAmount describes the maximum signed 32-bit integer value.
|
|
// It is not an actual tracked points value, but exists to permit reuse.
|
|
SharedBalanceAmount = 1000
|
|
|
|
// WiinoMaApplicationID is the title ID for the Japanese channel Wii no Ma.
|
|
WiinoMaApplicationID = "000100014843494A"
|
|
// WiinoMaServiceTitleID is the service ID used by Wii no Ma's theatre.
|
|
WiinoMaServiceTitleID = "000101006843494A"
|
|
)
|
|
|
|
// contentAesKey is the AES key that is used to encrypt title contents.
|
|
var contentAesKey = [16]byte{0x72, 0x95, 0xDB, 0xC0, 0x47, 0x3C, 0x90, 0x0B, 0xB5, 0x94, 0x19, 0x9C, 0xB5, 0xBC, 0xD3, 0xDC}
|
|
|
|
func getBalance(e *Envelope) Balance {
|
|
var points string
|
|
err := pool.QueryRow(ctx, QueryPointsStatement, e.DeviceId()).Scan(&points)
|
|
if err != nil {
|
|
e.Error(104, "Could not retrieve points balance.", err)
|
|
}
|
|
log.Printf("Points balance for device %s: %s", e.DeviceId(), points)
|
|
if points == "" {
|
|
points = "0"
|
|
}
|
|
pointsInt, err := strconv.Atoi(points)
|
|
if err != nil {
|
|
e.Error(114, "Could not convert points balance to integer.", err)
|
|
}
|
|
log.Printf("Points balance for device %s: %d", e.DeviceId(), pointsInt)
|
|
return Balance{
|
|
Amount: pointsInt,
|
|
Currency: "POINTS",
|
|
}
|
|
}
|
|
|
|
func checkDeviceStatus(e *Envelope) {
|
|
e.AddCustomType(getBalance(e))
|
|
e.AddKVNode("ForceSyncTime", "0")
|
|
e.AddKVNode("ExtTicketTime", "0")
|
|
e.AddKVNode("SyncTime", e.Timestamp())
|
|
|
|
}
|
|
|
|
func notifyETicketsSynced(e *Envelope) {
|
|
// TODO: Implement handling of synchronization timing
|
|
|
|
}
|
|
|
|
func listETickets(e *Envelope) {
|
|
|
|
/* var titleIds [5]string
|
|
var ticketIds [5]string
|
|
titleIds[0] = "0001000144574641"
|
|
ticketIds[0] = "0001000000000000"
|
|
titleIds[1] = "000100014F484243"
|
|
ticketIds[1] = "0023DB4424D033EC"
|
|
titleIds[2] = "0001000452464E45"
|
|
ticketIds[2] = "00019E9781E7E89F"
|
|
titleIds[3] = "0001000452465045"
|
|
ticketIds[3] = "0001F72A2763BFA2"
|
|
titleIds[4] = "00010004524D4345"
|
|
ticketIds[4] = "00019B1950DF85DF"
|
|
for i := 0; i < len(titleIds); i++ {
|
|
e.AddCustomType(Tickets{
|
|
TitleId: titleIds[i],
|
|
Version: 1,
|
|
|
|
// We do not support migration, ticket IDs, or revocation.
|
|
TicketId: ticketIds[i],
|
|
RevokeDate: 0,
|
|
MigrateCount: 0,
|
|
MigrateLimit: 0,
|
|
})
|
|
}
|
|
/*e.AddCustomType(Tickets{
|
|
TitleId: titleIds[2],
|
|
Version: 1,
|
|
|
|
// We do not support migration, ticket IDs, or revocation.
|
|
TicketId: "0",
|
|
RevokeDate: 0,
|
|
MigrateCount: 0,
|
|
MigrateLimit: 0,
|
|
})*/
|
|
|
|
e.AddKVNode("ForceSyncTime", "0")
|
|
e.AddKVNode("ExtTicketTime", "0")
|
|
e.AddKVNode("SyncTime", e.Timestamp())
|
|
}
|
|
|
|
func getETickets(e *Envelope) {
|
|
/* var etickets [8]string
|
|
etickets[0] = "AAEAAVtvJd7vSdT1RY1l6q5kaCHrdVis2lLa0/25pbbHaqr560eOnp/VpWjhGfD1ElTDJN3vRgb+ijzM9NrnBQvW/oFXAfsAyhw/UuO3Av7rB6cdd4e2wmpraxIavl9XL8nVrsKVbtI8vH35HUzTDO46ES1M7+cYtCIAWToSJE+UtWK0PNPAlhJZ2poKT96kiq+9bGT1u+Tls7pVrgv8ZnT4dDKUIZkBUWntfCU84WVpvl953lC/8Xff/B29umu9jjKpfvmx436Oi9TpZE94zRdR2DLs0kxtyreV/uIjBYLv2eZcjzskXNoOTEe+3++V5yvNW201bgUwAhiTlIVx+AQwgScAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSb290LUNBMDAwMDAwMDEtWFMwMDAwMDAwMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALTS+bbifI4ioLdMFp2KjnlkPV+Gq0jq3IjvdPz6AEIHZUFmW9vJW+S/PyWqqdndg67xqOOy5EOHteuwAAAAXl16v6DlPOAaZ7b6wUSvqwAAAY7miaw1FQJ7CPUAAQAASEFaQf//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//////////////////////////////////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
|
|
etickets[1] = "AAEAATuy8VhF9O0027viWCvKomWmnnJVjhiuYsJ6ycMJzwRLn/o5IEY5v7MZiNY2Sn5+ixDfiean3Wny1Kq9DIMVwoERgCUkY4D051d4YPuPnraERmNU5oMU9DvZosCEdOWlihKYdUGyyCbk2r4uy9Sai+cv+ozru+FAB2LLsgG/t8U5ekKpbYHBB/Z1IfSJiqt7gg5Fn1o+NIeaMNZBYZHQU7tGjzYnkCGJQj00LusAnz76gtOlfYms9mpHr0iWWlDWDYeYosJ1XTf8Swuu0zUf5po1l0li767/sZ/N8oYRSMok2CkXSwyQHlrRJqlmK4IOUIhRrUfsIlwkvp6WSxeBnl8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSb290LUNBMDAwMDAwMDEtWFMwMDAwMDAwMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALTS+bbifI4ioLdMFp2KjnlkPV+Gq0jq3IjvdPz6AEIHZUFmW9vJW+S/PyWqqdndg67xqOOy5EOHteuwAAAACtSjNHpZMSx0mtlhmYd1LgAAAdSMLJjFDAJ7CPUAAQABSENSRf//AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//////////////////////////////////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
|
|
//-----
|
|
//WiiSOAP eticket
|
|
//etickets[2] = "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"
|
|
//vWii eticket
|
|
etickets[2] = "AAEAASNTxfAL/ZyPDwPVv2IUZsPS/NbZxdcoiHUt5pR33+IWAJEmkgp3SudX/BhuCKTdzGC9gdBIXWNVdzlx5sn0RtmOSsZkazePp2tU+BRi3EpJ8Oiz2Sh03BrZmOayRJybVsc/OfhtbAJo0XOXVoHWXjx/UfBpcPIf4X7Sm4pQUdD0/xQAo0OoCnWVBf1bNYy1VRBN5srdnLu0qSaVX7zj6MqrjXs1JRxYokQ2l1u0/2/Tj1u89Kw2dzgm8vb77kdLJ58co+IJYTii3hsCySItp0t2yzWnWVfRsJFNjsXTDxPwo6Uw+5YfoBrR5+EiuT0MgD5CJIqlJraeI8maqiL7ySMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSb290LUNBMDAwMDAwMDEtWFMwMDAwMDAwMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALTS+bbifI4ioLdMFp2KjnlkPV+Gq0jq3IjvdPz6AEIHZUFmW9vJW+S/PyWqqdndg67xqOOy5EOHteuwAAAAAXc90P6nEAXs/JOIzaCuVwAAAepYrULdZQJ7CPUAAQABSEFURf//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//////////////////////////////////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
|
|
//-----
|
|
etickets[3] = "AAEAAafzenrwRhEO0HsLSaejzYM+Lm8QW8ahw1PR+pZFEY4YFNygeyDkDtEWf9/aq3y1YG+cEPy1Y5Mrn1fhomhDPKQpoDr8ou3ZiZkiYLBzAp1EfxUIpeLSVwY5AsQ55U1+cihFLBRxC3l3eYxCWqIsiAZm1kj/79cRYHoDGIhRvbGfHjgEFPLF73IeIqZn5kkkukuRjX4PXVe6lungGZuTnvgsb/TzrCVRZ8hjFmCINUiOHxtG4CvybJqxnXekl8hVRmIG3eDmiWuHfskN+jHIe4YjVQotZLIB7FcZr1Tk35XJN08re99cxJfEGC/SRHd/VMwn0/WFJDQh6KWjy4fX1K8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSb290LUNBMDAwMDAwMDEtWFMwMDAwMDAwMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALTS+bbifI4ioLdMFp2KjnlkPV+Gq0jq3IjvdPz6AEIHZUFmW9vJW+S/PyWqqdndg67xqOOy5EOHteuwAAAAhz/e0Ych6ONikIIpXPQq1gAAAapyxP88DQJ7CPUAAQABSEFKRf//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//////////////////////////////////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
|
|
etickets[4] = "AAEAAUth7dnIIm2kr6Bw3hZkkBCycZV+73NNOs95tTHFnYyO+GJ/zVOWF1s0Rne2PugqcPHWe9Skn6Z1gGmGdXMgNK5q0+QqWfoG/LkD4F8WvYO06ihcsXfDn9GTDDAcDfFTMWssApcpQeER18S/zG45tug+0ZnsayhmyBbyK6zJnZFm6Mxp9PsuuN79TRpK8pKpRPLXVjzKFUaY2Y3g+Tc+RHJ/ptDb+ONgP6iZ2kd9k81J1kugRn6DqisbC8ZKuEaHqeCQ3hLc9xKc2EfpVWW62grEbkWSZOM7ellar5BM0Hd3Ne3Xcsp8b1v4JFMVdRahrL94qVlFX7GugQubHK9tI4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSb290LUNBMDAwMDAwMDEtWFMwMDAwMDAwMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALTS+bbifI4ioLdMFp2KjnlkPV+Gq0jq3IjvdPz6AEIHZUFmW9vJW+S/PyWqqdndg67xqOOy5EOHteuwAAAAk+b/rdrXVvwTB4zE+vuiXgAAAUaiyG6pjQJ7CPUAAQABSEFQRf//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//////////////////////////////////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
|
|
etickets[5] = "AAEAAZzaP2lz+bZIy6fSW/ncG+8BnyYhX6+lyEwGDBMlyTInPeNxvEohK4jjhZzYK6uvV4PM5MPLu0e3rfcEqzYTVKVJI9sn06O+JuTFzEIzyV5OTRRNqAlwzG+6KFSwMxwH5qOfwRCoSYsy6vw+F0QAbwFcdvuN0BpRVhDZKu390GBCM6sKTFE881oX+q2RfhKnA+ZFsSYlklpOAHwdlzRrlgVgnfkHTGh0JgtgcNtvWsPgfEZqkj5lrrvINWwr+yWZKrIhcD1xRIWX2w8zKBCExyqdmIfMKPDR0+RLzi4EAMWcNEA3pDUoneKcxE/tlZ3m5A0heRSCfTlbeMcyra0fR0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSb290LUNBMDAwMDAwMDEtWFMwMDAwMDAwMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALTS+bbifI4ioLdMFp2KjnlkPV+Gq0jq3IjvdPz6AEIHZUFmW9vJW+S/PyWqqdndg67xqOOy5EOHteuwAAAARfNQD9UtTUOVSCUyDkupPAAAASdjIxILugJ7CPUAAQABSENTRf//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//////////////////////////////////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
|
|
etickets[6] = "AAEAAX8fEr0hDFGPJalcFmdQztiwR3Fb8tvPEAfeA5kO2Nbynyw2bhL4MAV54sGsxHfBlcm5kTNnH+V+aBum2I+U/Y9sxXQhqvlNJ9qjBNCAJBR8DIz8//biXPwbh0hQOp4FaH9yYfGVNw4m7qrKvFiSVvrCLS4SZJx5Ka8QjiS2smDtj++b7F78C2k6UK7guCRgnVoUTg4po/4+FOSdfkKEdOIKNXKfnocJcUsdmOBO4NLK73/lDdz3XPjWTPtg2/IQ2UGxp5oH1Z+W57jcWFfYgIDcXk4E1xtBd4s33om4RMnL4jvVUtxA2xx/ixQ7H2j3Inqk71lTqEZDHFckwoUY9OUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSb290LUNBMDAwMDAwMDEtWFMwMDAwMDAwMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALTS+bbifI4ioLdMFp2KjnlkPV+Gq0jq3IjvdPz6AEIHZUFmW9vJW+S/PyWqqdndg67xqOOy5EOHteuwAAAAWnOfqtsyBhg0ReE0M9dVxwAAAV8mkF0mKAJ7CPUAAQABSEFXRQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//////////////////////////////////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
|
|
etickets[7] = "AAEAAR1dFX4o1WcYlRaHWYwiNPqKuGB6xHbZ46f8w9LBZXRkX/5P9ORxHKwjeWI0sAM3m0bCn4gR1et4821FrDw1gidnMcRqolZqvvtDhtRj3whdYbDgIii1lmpZPOOoWQnTKaKJp6/FUm+eytBB/wr9azJ69tyP3fp0Brg1m8dgRajg5gs2rTKOEpOeuCGMaQBB/a05TRf9zyfvOhL8kHQher8lB2AdkkL88AD2PqIDV4HeE82dk7hi/NI1HZVJejf5aOcswc3FhDNHFSBMKKBPGpVD7RHCjJVEoPXa7LhdZZ9nkpmgQaa1F8s06N5q/yD1rwtWnTyHca7U1z9BoTghn5EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSb290LUNBMDAwMDAwMDEtWFMwMDAwMDAwMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALTS+bbifI4ioLdMFp2KjnlkPV+Gq0jq3IjvdPz6AEIHZUFmW9vJW+S/PyWqqdndg67xqOOy5EOHteuwAAAAT2D7XouKddLkZlKJXqcuegAAtzdsE3eQLQJ7CPUAAQABSEFERf//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//////////////////////////////////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
|
|
//etickets[8] = "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"
|
|
var certs [2]string
|
|
// vWii certs
|
|
certs[0] = "AAEAALOtsyJrPD3/G0tAdxb/T3rXZIbIlaxWLSHxBgHU9mQoGRwHdo/fGuLOeyfJD7wK0DEleOwHebZX1DckE6f4bwwUwO9uCUHtKwXsOVc2B4kASoeNLp34x6Wp+MqzEbEYeVe7+JjiolQCz1Q5zyu/oOH4XAZug5rglMpH4BVY9W5vNOkqotw4k343zYxcTf0vEU/oaMmo2f7YbgwhdaK9fom5x7UT9Bp5YUQ5EO/51/5XIhjVbft/SXqky5DU8a6xduRoXaeUQGCYLwRIQB/Pxrrr2hYwtHO0FSM1CAcKn0+JeOYs7F6SRqWovaCFeGh1DDoRL6+V6DjImQ6HsWLNENqzMZZl74ibVBuzNrtnU5+vwq4tCi51wCN06k6sjZlQf1m5U3cwXyY1xgipkJOsj8beI7l66nC0xM9msw5YMg7FtnIESM47sRxTH8twKHy1wnxnT7v9jH/JQiCkcyMdWH5aGhqC43V5obuCbs4Bccl1Y0dLHUbmebKCN2IRzccAL0aHwjxtwNW1eG7h8nP/AZJQD/THUGrucrb0PfYI/qWDofmGD4evUkRUu0fDBgyU6Zv31jKnyKtLT/U1IR/BgEe7evpaK9e4hK2OVk9bif83lzfx9QE7H57EGG+SKtXEs8DVhwucBK8atfO8bQrxfUcI5EPpc/e3cHdUuvPs0qxJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUm9vdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFDQTAwMDAwMDAxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW/p9XLJ5yeLu4SHG6vRP9jn4jweLS3ftn5VgsDWCgbUOVatyERWhd3A8ejD+OunvHGC8HZdGdrI6aMwEsZhSW8lo8R3i21Dk2efwceVi2uIJIjPp02P2HdfBn/OkqR6PZVPUcd17hLnxuM5zNfD1VAVjoeq4OWPgm+kBAR+ZVGNhKHAg6cwNq0h/FA1mJqGDbScRHyBo3kdyFJFRz2nGG6YO+dlJoPcfVJny05rSjHAFNIKTxDH/vTP2vKYNxxleorzFbSALr20G0JxB243pxyAVTKSDK2nAjGnNOwc6AGNgL0YtM4BhpepskVzVYjV5w+tkzkTvWG0UuqqINAGbPuvu03kAAQABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
|
|
certs[1] = "AAEAAX2dXrpSgdynBl0vCGjbisc6zn6pkfGWn+HQ8sEfrsDD8BrctEat5coDtiUhlGLG4UENueY/3pjRryY7TLKHhCeCcu8nE0uHwljWe2Lytb+ctrqMiRkuxQaJrHQkoCIJQAPumKS9LwE7WT/lZmzV61rXpJMQ8077tD1Gy/G1I8+C9o61bbkEp8KoK+EdeNObog2Q0wdC2156we/yIVEJYs+pFKiA3PQXupmTCu4IsLDlGj6fr83C1+PLoS86wAeQ3kR6w8U4qGeSOAeL1MSyRawpFohtKg5ZTu1cyDVpi01iON8Fck3M9oGAinB0Blkwv/hRQTfoFfq6oXK44GlsYeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSb290LUNBMDAwMDAwMDEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVhTMDAwMDAwMDMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADxuJ/RrQepN4p7EAx9xzm+nt23MgCJqyWx+HGvWqn0WJ7RgwIyjoEaH+/QCcgGNkP4VLnhO7thOnrPhxSFa6Rbque7xk6y912H6/Jn7Q+kQakzZl5XfVreq/tGLnYAypzpTcTLmDmSq3ovs6Oeor+cU+zQ3Ppri16yy6QP+kB1+PKy3pc4EYct9eKmw4sv3I5X3b1fRusn1hlS9q74YrfumsaCorGaqbVY++uziS+9UMn13EpunJv+RYA0qUIYLd63X+DRs98Ol+OZgIdwGMKyg/E1dXxaMPw/MISkmqrAHucGaU+OFEjaEjrMT/omqjj3778njzaXeXddt8Wtx4mR3PhDjQABAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
|
//WiiSOAP certs
|
|
//certs[0] = "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"
|
|
//certs[1] = "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"
|
|
for i := 0; i < len(etickets); i++ {
|
|
e.AddKVNode("ETickets", etickets[i])
|
|
}
|
|
for i := 0; i < len(certs); i++ {
|
|
e.AddKVNode("Certs", certs[i])
|
|
}*/
|
|
e.AddKVNode("ForceSyncTime", "0")
|
|
e.AddKVNode("ExtTicketTime", e.Timestamp())
|
|
e.AddKVNode("SyncTime", e.Timestamp())
|
|
}
|
|
|
|
func purchaseTitle(e *Envelope) {
|
|
accountId, err := e.AccountId()
|
|
if err != nil {
|
|
e.Error(2, "missing account ID", err)
|
|
return
|
|
}
|
|
|
|
tempItemId, err := e.getKey("ItemId")
|
|
if err != nil {
|
|
e.Error(2, "missing item ID", err)
|
|
return
|
|
}
|
|
|
|
// Our struct takes an integer rather than a string.
|
|
itemId, _ := strconv.Atoi(tempItemId)
|
|
|
|
// Determine the title ID we're going to purchase.
|
|
titleId, err := e.getKey("TitleId")
|
|
if err != nil {
|
|
e.Error(2, "missing account ID", err)
|
|
return
|
|
}
|
|
|
|
ticket := new(bytes.Buffer)
|
|
var ticketStruct wadlib.Ticket
|
|
err = binary.Read(bytes.NewReader(wadlib.TicketTemplate), binary.BigEndian, &ticketStruct)
|
|
if err != nil {
|
|
// Should never happen but report
|
|
e.Error(2, "error reading ticket template", err)
|
|
return
|
|
}
|
|
|
|
// We will now formulate the ticket for this title.
|
|
intTitleId, err := strconv.ParseUint(titleId, 16, 64)
|
|
if err != nil {
|
|
e.Error(2, "invalid title id", err)
|
|
return
|
|
}
|
|
|
|
ticketStruct.TitleID = intTitleId
|
|
|
|
// Title key is encrypted with the common key and current title ID
|
|
ticketStruct.UpdateTitleKey(contentAesKey)
|
|
tikFile, err := os.ReadFile("./tickets/" + strings.ToLower(strings.Replace(titleId, "00010001", "", 1)) + ".tik")
|
|
fmt.Println("ticket name: " + strings.ToLower(strings.Replace(titleId, "00010001", "", 1)) + ".tik")
|
|
if err != nil {
|
|
tikFile, err = os.ReadFile("/media/sdc1/hydrobleach/Local/ccs.cdn.shop.wii.com/ccs/download/" + titleId + "/cetk")
|
|
fmt.Println("ticket name: " + titleId + ".tik")
|
|
}
|
|
/*wad, err := wadlib.LoadWADFromFile("../WiiLikeToParty/wad/ticket/" + strings.ToLower(titleId) + "_bogus.wad")
|
|
if err != nil {
|
|
e.Error(2, "couldn't read wad", err)
|
|
return
|
|
}
|
|
wadTicket, err := wad.GetTicket()
|
|
if err != nil {
|
|
e.Error(2, "couldn't read ticket", err)
|
|
return
|
|
}*/
|
|
/*wad, err := wadlib.LoadWADFromFile("./smash bros.wad")
|
|
if err != nil {
|
|
e.Error(2, "couldn't read wad", err)
|
|
return
|
|
}
|
|
wad.ChangeTitleKey(contentAesKey)
|
|
tik, err := wad.GetTicket()
|
|
if err != nil {
|
|
e.Error(2, "couldn't read ticket", err)
|
|
return
|
|
}
|
|
os.WriteFile("content/cetk", tik, 0777)
|
|
|
|
wadBytes, err := wad.GetWAD(wadlib.WADTypeCommon)
|
|
if err != nil {
|
|
e.Error(2, "couldn't get wad", err)
|
|
return
|
|
}
|
|
|
|
wadContent1, err := wad.GetContent(0)
|
|
os.WriteFile("content/00000000", wadContent1, 0777)
|
|
wadContent2, err := wad.GetContent(1)
|
|
os.WriteFile("content/00000001", wadContent2, 0777)
|
|
wadContent3, err := wad.GetContent(2)
|
|
os.WriteFile("content/00000002", wadContent3, 0777)
|
|
wadContent4, err := wad.GetContent(3)
|
|
os.WriteFile("content/00000003", wadContent4, 0777)
|
|
wadContent5, err := wad.GetContent(4)
|
|
os.WriteFile("content/00000004", wadContent5, 0777)
|
|
wadContent6, err := wad.GetContent(5)
|
|
os.WriteFile("content/00000005", wadContent6, 0777)
|
|
wadContent7, err := wad.GetContent(6)
|
|
os.WriteFile("content/00000006", wadContent7, 0777)
|
|
wadContent8, err := wad.GetContent(7)
|
|
os.WriteFile("content/00000007", wadContent8, 0777)
|
|
wadTmd, err := wad.GetTMD()
|
|
os.WriteFile("content/tmd", wadTmd, 0777)
|
|
os.WriteFile("smashnew.wad", wadBytes[:], 0777)*/
|
|
|
|
//version := 0
|
|
if titleId == WiinoMaServiceTitleID {
|
|
// Wii no Ma needs the ticket to be in the v1 ticket format.
|
|
// Update the ticket to reflect that.
|
|
ticketStruct.FileVersion = 1
|
|
ticketStruct.AccessTitleMask = math.MaxUint32
|
|
ticketStruct.LicenseType = 5
|
|
|
|
err = binary.Write(ticket, binary.BigEndian, ticketStruct)
|
|
if err != nil {
|
|
e.Error(2, "failed to create ticket", err)
|
|
return
|
|
}
|
|
|
|
refId, err := e.getKey("ReferenceId")
|
|
if err != nil {
|
|
e.Error(2, "missing reference ID", err)
|
|
return
|
|
}
|
|
|
|
// Convert reference ID to bytes
|
|
refIdBytes, err := hex.DecodeString(refId)
|
|
log.Printf("refIdBytes err: %b", refIdBytes)
|
|
if err != nil {
|
|
log.Printf("unexpected error converting reference id to bytes: %v", err)
|
|
e.Error(2, "error purchasing", nil)
|
|
return
|
|
}
|
|
|
|
var referenceId [16]byte
|
|
copy(referenceId[:], refIdBytes)
|
|
|
|
subscriptions := []v1Ticket.V1SubscriptionRecord{
|
|
{
|
|
ExpirationTime: uint32(time.Now().AddDate(0, 1, 0).Unix()),
|
|
ReferenceID: referenceId,
|
|
},
|
|
}
|
|
|
|
// Query the database for other purchased items of the same title id.
|
|
rows, err := pool.Query(ctx, QueryOwnedServiceTitles, titleId, accountId)
|
|
if err != nil {
|
|
log.Printf("unexpected error purchasing: %v", err)
|
|
e.Error(2, "error purchasing", nil)
|
|
return
|
|
}
|
|
|
|
defer rows.Close()
|
|
for rows.Next() {
|
|
var currentRefIdString string
|
|
var purchasedTime time.Time
|
|
err = rows.Scan(¤tRefIdString, &purchasedTime, nil)
|
|
if err != nil {
|
|
log.Printf("unexpected error purchasing: %v", err)
|
|
e.Error(2, "error purchasing", nil)
|
|
return
|
|
}
|
|
|
|
refIdBytes, err = hex.DecodeString(currentRefIdString)
|
|
if err != nil {
|
|
log.Printf("unexpected error converting reference id to bytes: %v", err)
|
|
e.Error(2, "error purchasing", nil)
|
|
return
|
|
}
|
|
|
|
var currentReferenceId [16]byte
|
|
copy(currentReferenceId[:], refIdBytes)
|
|
subscriptions = append(subscriptions, v1Ticket.V1SubscriptionRecord{
|
|
ExpirationTime: uint32(purchasedTime.AddDate(0, 0, 30).Unix()),
|
|
ReferenceID: currentReferenceId,
|
|
})
|
|
}
|
|
|
|
newTicket, err := v1Ticket.CreateV1Ticket(ticket.Bytes(), subscriptions)
|
|
if err != nil {
|
|
log.Printf("unexpected error creating v1Ticket: %v", err)
|
|
e.Error(2, "error creating ticket", nil)
|
|
return
|
|
}
|
|
|
|
ticket = bytes.NewBuffer(newTicket)
|
|
} else {
|
|
// Validate that this title exists.
|
|
app, err := GetOSCApp(titleId)
|
|
if err != nil {
|
|
e.Error(2, "an error has occurred retrieving app metadata", err)
|
|
return
|
|
}
|
|
|
|
if app == nil {
|
|
e.Error(2, "title does not exist", nil)
|
|
return
|
|
}
|
|
|
|
err = binary.Write(ticket, binary.BigEndian, ticketStruct)
|
|
if err != nil {
|
|
e.Error(2, "failed to create ticket", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
// Associate the given title ID with the user.
|
|
//_, err = pool.Exec(ctx, AssociateTicketStatement, accountId, titleId, version, itemId, time.Now().UTC())
|
|
//if err != nil {
|
|
// log.Printf("unexpected error purchasing: %v", err)
|
|
// e.Error(2, "error purchasing", nil)
|
|
//}
|
|
amount, err := e.getKey("Amount")
|
|
if err != nil {
|
|
e.Error(2, "couldn't get amount", err)
|
|
}
|
|
amount = strings.ReplaceAll(amount, ".", "")
|
|
|
|
amountInt, err := strconv.Atoi(amount)
|
|
if err != nil {
|
|
e.Error(2, "couldn't convert amount to integer", err)
|
|
}
|
|
// The returned ticket is expected to have two other certificates associated.
|
|
//ticketString := b64(append(ticket.Bytes(), wadlib.CertChainTemplate...))
|
|
ticketString := b64(tikFile)
|
|
//var pointsToRemove int
|
|
e.AddCustomType(getBalance(e))
|
|
e.AddCustomType(Transactions{
|
|
TransactionId: "00000000",
|
|
Date: e.Timestamp(),
|
|
Type: "PURCHGAME",
|
|
TotalPaid: amountInt,
|
|
Currency: "POINTS",
|
|
ItemId: itemId,
|
|
ItemPricing: Prices{
|
|
ItemId: itemId,
|
|
Price: Price{Amount: amountInt, Currency: "POINTS"},
|
|
Limits: LimitStruct(PR),
|
|
LicenseKind: PERMANENT,
|
|
},
|
|
})
|
|
var points string
|
|
deviceToken, err := e.getKey("DeviceToken")
|
|
if err != nil {
|
|
e.Error(2, "missing device token", err)
|
|
return
|
|
}
|
|
deviceToken = strings.Split(deviceToken, "-")[1]
|
|
md5DeviceToken := fmt.Sprintf("%x", md5.Sum([]byte(deviceToken)))
|
|
serialNo, err := e.getKey("SerialNo")
|
|
if err != nil {
|
|
e.Error(2, "missing serial", err)
|
|
return
|
|
}
|
|
points = strconv.Itoa(getBalance(e).Amount - amountInt)
|
|
|
|
_, err = pool.Exec(ctx, AssociatePointsStatement, e.DeviceId(), deviceToken, md5DeviceToken, accountId, e.region, serialNo, points)
|
|
if err != nil {
|
|
e.Error(103, "error calculating points", err)
|
|
}
|
|
e.AddKVNode("SyncTime", e.Timestamp())
|
|
//log.Println(ticketString)
|
|
e.AddKVNode("ETickets", ticketString)
|
|
//e.AddKVNode("ETickets", "AAEAAR1dFX4o1WcYlRaHWYwiNPqKuGB6xHbZ46f8w9LBZXRkX/5P9ORxHKwjeWI0sAM3m0bCn4gR1et4821FrDw1gidnMcRqolZqvvtDhtRj3whdYbDgIii1lmpZPOOoWQnTKaKJp6/FUm+eytBB/wr9azJ69tyP3fp0Brg1m8dgRajg5gs2rTKOEpOeuCGMaQBB/a05TRf9zyfvOhL8kHQher8lB2AdkkL88AD2PqIDV4HeE82dk7hi/NI1HZVJejf5aOcswc3FhDNHFSBMKKBPGpVD7RHCjJVEoPXa7LhdZZ9nkpmgQaa1F8s06N5q/yD1rwtWnTyHca7U1z9BoTghn5EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSb290LUNBMDAwMDAwMDEtWFMwMDAwMDAwMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALTS+bbifI4ioLdMFp2KjnlkPV+Gq0jq3IjvdPz6AEIHZUFmW9vJW+S/PyWqqdndg67xqOOy5EOHteuwAAAAT2D7XouKddLkZlKJXqcuegAAtzdsE3eQLQJ7CPUAAQABSEFERf//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//////////////////////////////////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==")
|
|
// Two cert types must be present.
|
|
e.AddKVNode("Certs", b64(wadlib.CertChainTemplate))
|
|
e.AddKVNode("Certs", b64(wadlib.CertChainTemplate))
|
|
e.AddKVNode("TitleId", titleId)
|
|
}
|
|
|
|
func listPurchaseHistory(e *Envelope) {
|
|
accountId, err := e.AccountId()
|
|
if err != nil {
|
|
e.Error(2, "missing account ID", err)
|
|
return
|
|
}
|
|
|
|
titleId, err := e.getKey("ApplicationId")
|
|
if err != nil {
|
|
e.Error(2, "missing application ID", err)
|
|
return
|
|
}
|
|
|
|
var transactions []Transactions
|
|
if titleId == WiinoMaApplicationID {
|
|
// We will query the database differently for Wii no Ma.
|
|
rows, err := pool.Query(ctx, QueryOwnedServiceTitles, WiinoMaServiceTitleID, accountId)
|
|
if err != nil {
|
|
log.Printf("unexpected error querying owned service titles: %v", err)
|
|
e.Error(2, "error purchasing", nil)
|
|
return
|
|
}
|
|
|
|
defer rows.Close()
|
|
for rows.Next() {
|
|
var refId string
|
|
var purchasedTime time.Time
|
|
var itemId int
|
|
err = rows.Scan(&refId, &purchasedTime, &itemId)
|
|
if err != nil {
|
|
log.Printf("unexpected error purchasing: %v", err)
|
|
e.Error(2, "error purchasing", nil)
|
|
return
|
|
}
|
|
|
|
transaction := Transactions{
|
|
TransactionId: "00000000",
|
|
// (Sketch) I don't know why but Wii no Ma won't acknowledge the entry if it isn't past a day from
|
|
// purchase.
|
|
Date: strconv.Itoa(int(purchasedTime.AddDate(0, 0, -1).UnixMilli())),
|
|
Type: "PURCHGAME",
|
|
TotalPaid: 0,
|
|
Currency: "POINTS",
|
|
ItemId: itemId,
|
|
ItemPricing: Prices{
|
|
ItemId: itemId,
|
|
Price: Price{
|
|
Amount: 0,
|
|
Currency: "POINTS",
|
|
},
|
|
Limits: LimitStruct(PR),
|
|
LicenseKind: SERVICE,
|
|
},
|
|
TitleId: WiinoMaServiceTitleID,
|
|
ItemCode: itemId,
|
|
ReferenceId: refId,
|
|
}
|
|
|
|
transactions = append(transactions, transaction)
|
|
}
|
|
} else {
|
|
transactions = append(transactions, Transactions{
|
|
TransactionId: "00000000",
|
|
// Is timestamp in milliseconds, placeholder one is Wed Oct 19 2022 18:02:46
|
|
Date: "1666202566218",
|
|
Type: "PURCHGAME",
|
|
TotalPaid: 0,
|
|
Currency: "POINTS",
|
|
ItemId: 0,
|
|
ItemPricing: Prices{
|
|
ItemId: 0,
|
|
Price: Price{
|
|
Amount: 0,
|
|
Currency: "POINTS",
|
|
},
|
|
Limits: LimitStruct(PR),
|
|
LicenseKind: PERMANENT,
|
|
},
|
|
TitleId: "000101006843494A",
|
|
})
|
|
}
|
|
|
|
e.AddCustomType(transactions)
|
|
e.AddKVNode("ListResultTotalSize", strconv.Itoa(len(transactions)))
|
|
}
|
|
|
|
// genServiceUrl returns a URL with the given service against a configured URL.
|
|
// Given a baseUrl of example.com and genServiceUrl("ias", "IdentityAuthenticationSOAP"),
|
|
// it would return http://ias.example.com/ias/services/ias/IdentityAuthenticationSOAP.
|
|
func genServiceUrl(service string, path string) string {
|
|
return fmt.Sprintf("http://%s.%s/%s/services/%s", service, baseUrl, service, path)
|
|
}
|
|
|
|
func getECConfig(e *Envelope) {
|
|
contentUrl := fmt.Sprintf("http://ccs.%s/ccs/download", baseUrl)
|
|
e.AddKVNode("ContentPrefixURL", contentUrl)
|
|
e.AddKVNode("UncachedContentPrefixURL", contentUrl)
|
|
e.AddKVNode("SystemContentPrefixURL", contentUrl)
|
|
e.AddKVNode("SystemUncachedContentPrefixURL", contentUrl)
|
|
|
|
e.AddKVNode("EcsURL", genServiceUrl("ecs", "ECommerceSOAP"))
|
|
e.AddKVNode("IasURL", genServiceUrl("ias", "IdentityAuthenticationSOAP"))
|
|
e.AddKVNode("CasURL", genServiceUrl("cas", "CatalogingSOAP"))
|
|
e.AddKVNode("NusURL", genServiceUrl("nus", "NetUpdateSOAP"))
|
|
}
|
|
|
|
func purchasePoints(e *Envelope) {
|
|
deviceToken, err := e.getKey("DeviceToken")
|
|
if err != nil {
|
|
e.Error(2, "missing device token", err)
|
|
return
|
|
}
|
|
deviceToken = strings.Split(deviceToken, "-")[1]
|
|
md5DeviceToken := fmt.Sprintf("%x", md5.Sum([]byte(deviceToken)))
|
|
accountId, err := e.getKey("AccountId")
|
|
if err != nil {
|
|
e.Error(2, "missing account ID", err)
|
|
return
|
|
}
|
|
serialNo, err := e.getKey("SerialNo")
|
|
if err != nil {
|
|
e.Error(2, "missing serial", err)
|
|
return
|
|
}
|
|
itemId, err := e.getKey("ItemId")
|
|
if err != nil {
|
|
e.Error(2, "missing item ID", err)
|
|
return
|
|
}
|
|
amount, err := e.getKey("Amount")
|
|
if err != nil {
|
|
e.Error(2, "couldn't get amount", err)
|
|
}
|
|
currency, err := e.getKey("Currency")
|
|
if err != nil {
|
|
e.Error(2, "couldn't get currency", err)
|
|
}
|
|
var pointsToAdd int
|
|
var points string
|
|
if itemId == "100008" {
|
|
pointsToAdd = 1000
|
|
} else if itemId == "100030" {
|
|
pointsToAdd = 2000
|
|
} else if itemId == "100031" {
|
|
pointsToAdd = 3000
|
|
} else if itemId == "100032" {
|
|
pointsToAdd = 5000
|
|
}
|
|
points = strconv.Itoa(getBalance(e).Amount + pointsToAdd)
|
|
|
|
_, err = pool.Exec(ctx, AssociatePointsStatement, e.DeviceId(), deviceToken, md5DeviceToken, accountId, e.region, serialNo, points)
|
|
if err != nil {
|
|
e.Error(113, "error purchasing points", err)
|
|
}
|
|
itemIdStr, err := strconv.Atoi(itemId)
|
|
if err != nil {
|
|
e.Error(201, "couldn't convert item id to string", err)
|
|
}
|
|
rand.NewSource(time.Now().UnixNano())
|
|
// Generate a random 8-digit integer
|
|
transactionId := 10000000 + rand.Intn(90000000)
|
|
e.AddCustomType(PointsPurchaseInfo{
|
|
Transactions: PointsTransactions{
|
|
TransactionId: strconv.Itoa(transactionId),
|
|
Date: e.Timestamp(),
|
|
Type: "PURCHPOINTS",
|
|
TotalPaid: amount,
|
|
Currency: currency,
|
|
ItemId: itemId,
|
|
ItemPricing: GiftPrices{
|
|
ItemId: itemIdStr,
|
|
Price: GiftPrice{
|
|
Amount: amount,
|
|
Currency: currency,
|
|
},
|
|
},
|
|
},
|
|
})
|
|
}
|
|
func checkAccountBalance(e *Envelope) {
|
|
e.AddCustomType(getBalance(e))
|
|
}
|
|
func giftTitle(e *Envelope) {
|
|
accountId, err := e.getKey("AccountId")
|
|
if err != nil {
|
|
e.Error(1, "missing mandatory key named AccountId", err)
|
|
}
|
|
titleId, err := e.getKey("TitleId")
|
|
if err != nil {
|
|
e.Error(1, "missing mandatory key named TitleId", err)
|
|
}
|
|
notes, err := e.getKey("Notes")
|
|
if err != nil {
|
|
e.Error(1, "missing mandatory key named Notes", err)
|
|
}
|
|
unescapedNotes := html.UnescapeString(notes)
|
|
doc, err := xmlquery.Parse(strings.NewReader(unescapedNotes))
|
|
if err != nil {
|
|
fmt.Println("Error:", err)
|
|
return
|
|
}
|
|
senderFCNode := xmlquery.FindOne(doc, "//DeviceCode")
|
|
var senderFC string
|
|
if senderFCNode != nil {
|
|
senderFC = senderFCNode.InnerText()
|
|
} else {
|
|
e.Error(124, "Cannot find sender friend code", nil)
|
|
}
|
|
amount, err := e.getKey("Amount")
|
|
if err != nil {
|
|
e.Error(2, "couldn't get amount", err)
|
|
}
|
|
amount = strings.ReplaceAll(amount, ".", "")
|
|
log.Println("New amount: " + amount)
|
|
amountInt, err := strconv.Atoi(amount)
|
|
if err != nil {
|
|
e.Error(2, "couldn't convert amount to integer", err)
|
|
}
|
|
|
|
rand.NewSource(time.Now().UnixNano())
|
|
// Generate a random 8-digit integer
|
|
transactionId := 10000000 + rand.Intn(90000000)
|
|
log.Println(transactionId)
|
|
_, err = pool.Exec(ctx, AssociateGiftedTitleStatement, titleId, strconv.Itoa(transactionId+1), senderFC)
|
|
if err != nil {
|
|
e.Error(123, "error putting title in gifted titles table", err)
|
|
}
|
|
|
|
var points string
|
|
deviceToken, err := e.getKey("DeviceToken")
|
|
if err != nil {
|
|
e.Error(2, "missing device token", err)
|
|
return
|
|
}
|
|
deviceToken = strings.Split(deviceToken, "-")[1]
|
|
md5DeviceToken := fmt.Sprintf("%x", md5.Sum([]byte(deviceToken)))
|
|
serialNo, err := e.getKey("SerialNo")
|
|
if err != nil {
|
|
e.Error(2, "missing serial", err)
|
|
return
|
|
}
|
|
points = strconv.Itoa(getBalance(e).Amount - amountInt)
|
|
|
|
_, err = pool.Exec(ctx, AssociatePointsStatement, e.DeviceId(), deviceToken, md5DeviceToken, accountId, e.region, serialNo, points)
|
|
if err != nil {
|
|
e.Error(133, "error calculating points", err)
|
|
}
|
|
|
|
e.AddCustomType(getBalance(e))
|
|
e.AddCustomType(GiftTransactions{
|
|
TransactionId: strconv.Itoa(transactionId),
|
|
Date: e.Timestamp(),
|
|
Type: "PGIFTGAME",
|
|
})
|
|
e.AddCustomType(GiftTransactions{
|
|
TransactionId: strconv.Itoa(transactionId + 1),
|
|
Date: e.Timestamp(),
|
|
Type: "RGIFTGAME",
|
|
})
|
|
}
|
|
|
|
func acceptGiftTitle(e *Envelope) {
|
|
titleId, err := e.getKey("TitleId")
|
|
if err != nil {
|
|
e.Error(2, "missing mandatory key named TitleId", err)
|
|
}
|
|
accept, err := e.getKey("Accept")
|
|
if err != nil {
|
|
e.Error(2, "missing mandatory key named Accept", err)
|
|
}
|
|
transId, err := e.getKey("TransactionId")
|
|
if err != nil {
|
|
e.Error(2, "missing mandatory key named TransactionId", err)
|
|
}
|
|
if accept != "1" {
|
|
e.Error(10, "not accepting", nil)
|
|
}
|
|
|
|
_, err = pool.Exec(ctx, RemoveGiftedTitleStatement, titleId, transId)
|
|
if err != nil {
|
|
e.Error(143, "error removing title from database", err)
|
|
}
|
|
//Probably need to add ETicket, but will do later.
|
|
|
|
}
|